Группировать и агрегировать по нескольким свойствам на основе сложного совпадающего ключа - PullRequest
6 голосов
/ 26 января 2020

Я новичок в Java и работаю над проблемой, когда мне нужно сгруппировать и агрегировать коллекцию на основе приведенного ниже ключа соответствия, этот ключ представляет собой комбинацию свойств из нижеуказанных классов.

class MyKey {    
      String planYearMonth;
      String carSeries;
      String weekNo;
      String factoryCode;
      String lineClass;
      String frameSortCode;
      String ocfClassificationCode;
      String locationIdentificationCode;
      String carGroup;
      //setters & getters
      //equals & hashcode
    }

public class OCFIdentificationInfo {    
    private String frameSortCode;   
    private String ocfClassificationCode;   
    private String locationIdentificationCode;  
    private String carGroup;    
    private String frameCode;   
    //getters 
    //setters
    //hashCode
    //equals
    //toString
}

public class DailyOCF { 
    private String planYearMonth;   
    private String carSeries;   
    private String weekNo;  
    private String day; 
    private String factoryCode; 
    private String lineClass;   
    private OCFIdentificationInfo ocfInfo   
    private int maxQty = 0; 
    private int actualQty = 0;  
    //getters 
    //setters
    //hashCode
    //equals
    //toString
}

Коллекция DailyOCF заполняется в ArrayList, примеры данных приведены ниже.

DailyOCF [planYearMonth=201304, carSeries=K01, weekNo=17, day=, factoryCode=BBB, lineClass=1, ocfInfo=OCFIdentificationInfo [frameSortCode=00, ocfClassificationCode=MICRA   , locationIdentificationCode=XXX, carGroup=YYY, frameCode=00], maxQty=20, actualQty=0]
DailyOCF [planYearMonth=201304, carSeries=K01, weekNo=17, day=, factoryCode=BBB, lineClass=1, ocfInfo=OCFIdentificationInfo [frameSortCode=10, ocfClassificationCode=SEDAN   , locationIdentificationCode=XXX, carGroup=YYY, frameCode=10], maxQty=20, actualQty=0]
DailyOCF [planYearMonth=201304, carSeries=K01, weekNo=17, day=, factoryCode=BBB, lineClass=1, ocfInfo=OCFIdentificationInfo [frameSortCode=90, ocfClassificationCode=HDD navi, locationIdentificationCode=XXX, carGroup=YYY, frameCode=90], maxQty=3, actualQty=0]
DailyOCF [planYearMonth=201304, carSeries=K01, weekNo=17, day=, factoryCode=BBB, lineClass=1, ocfInfo=OCFIdentificationInfo [frameSortCode=00, ocfClassificationCode=MICRA   , locationIdentificationCode=XXX, carGroup=YYY, frameCode=00], maxQty=20, actualQty=0]
DailyOCF [planYearMonth=201304, carSeries=K01, weekNo=17, day=, factoryCode=BBB, lineClass=1, ocfInfo=OCFIdentificationInfo [frameSortCode=10, ocfClassificationCode=SEDAN   , locationIdentificationCode=XXX, carGroup=YYY, frameCode=10], maxQty=20, actualQty=0]
DailyOCF [planYearMonth=201304, carSeries=K01, weekNo=17, day=, factoryCode=BBB, lineClass=1, ocfInfo=OCFIdentificationInfo [frameSortCode=60, ocfClassificationCode=GGRADE  , locationIdentificationCode=XXX, carGroup=YYY, frameCode=60], maxQty=10, actualQty=0]
DailyOCF [planYearMonth=201304, carSeries=K01, weekNo=17, day=, factoryCode=BBB, lineClass=1, ocfInfo=OCFIdentificationInfo [frameSortCode=90, ocfClassificationCode=HDD navi, locationIdentificationCode=XXX, carGroup=YYY, frameCode=90], maxQty=3, actualQty=0]

Приведенный выше список необходимо сгруппировать на основе вышеупомянутого соответствующего ключа, после того как список должен быть похожим на приведенное ниже.

DailyOCF [planYearMonth=201304, carSeries=K01, weekNo=17, day=, factoryCode=BBB, lineClass=1, ocfInfo=OCFIdentificationInfo [frameSortCode=90, ocfClassificationCode=HDD navi, locationIdentificationCode=XXX, carGroup=YYY, frameCode=90], maxQty=3, actualQty=2]
DailyOCF [planYearMonth=201304, carSeries=K01, weekNo=17, day=, factoryCode=BBB, lineClass=1, ocfInfo=OCFIdentificationInfo [frameSortCode=60, ocfClassificationCode=GGRADE  , locationIdentificationCode=XXX, carGroup=YYY, frameCode=60], maxQty=10, actualQty=1]
DailyOCF [planYearMonth=201304, carSeries=K01, weekNo=17, day=, factoryCode=BBB, lineClass=1, ocfInfo=OCFIdentificationInfo [frameSortCode=10, ocfClassificationCode=SEDAN   , locationIdentificationCode=XXX, carGroup=YYY, frameCode=10], maxQty=20, actualQty=2]
DailyOCF [planYearMonth=201304, carSeries=K01, weekNo=17, day=, factoryCode=BBB, lineClass=1, ocfInfo=OCFIdentificationInfo [frameSortCode=00, ocfClassificationCode=MICRA   , locationIdentificationCode=XXX, carGroup=YYY, frameCode=00], maxQty=20, actualQty=2]

Поле actualQty необходимо агрегировать на основе соответствующего ключа.

Для этого я написал приведенный ниже код java, но не повезло Пожалуйста, посмотрите и дайте мне знать, подход подходит?

Map<MyKey, List<DailyOCF>> finalResult = dailyOCFList.stream()
  .collect(groupingBy(ocf -> new MyKey(ocf.planYearMonth, ocf.carSeries, ocf.weekNo, ocf.factoryCode,
      ocf.lineClass, ocf.ocfInfo.frameSortCode, ocf.ocfInfo.ocfClassificationCode,
      ocf.ocfInfo.locationIdentificationCode, ocf.ocfInfo.carGroup)));

Вот как список DailyOCF заполняется.

OCFIdentificationInfo info = new OCFIdentificationInfo();
info.setCarGroup("YYY");
info.setFrameCode("00");
info.setFrameSortCode("00");
info.setLocationIdentificationCode("XXX");
info.setOcfClassificationCode("MICRA");

OCFIdentificationInfo info1 = new OCFIdentificationInfo();
info1.setCarGroup("YYY");
info1.setFrameCode("10");
info1.setFrameSortCode("10");
info1.setLocationIdentificationCode("XXX");
info1.setOcfClassificationCode("SEDAN");

OCFIdentificationInfo info2 = new OCFIdentificationInfo();
info2.setCarGroup("YYY");
info2.setFrameCode("90");
info2.setFrameSortCode("90");
info2.setLocationIdentificationCode("XXX");
info2.setOcfClassificationCode("HDD navi");

OCFIdentificationInfo info3 = new OCFIdentificationInfo();
info3.setCarGroup("YYY");
info3.setFrameCode("00");
info3.setFrameSortCode("00");
info3.setLocationIdentificationCode("XXX");
info3.setOcfClassificationCode("MICRA");

OCFIdentificationInfo info4 = new OCFIdentificationInfo();
info4.setCarGroup("YYY");
info4.setFrameCode("10");
info4.setFrameSortCode("10");
info4.setLocationIdentificationCode("XXX");
info4.setOcfClassificationCode("SEDAN");

OCFIdentificationInfo info5 = new OCFIdentificationInfo();
info5.setCarGroup("YYY");
info5.setFrameCode("60");
info5.setFrameSortCode("60");
info5.setLocationIdentificationCode("XXX");
info5.setOcfClassificationCode("GGRADE");

OCFIdentificationInfo info6 = new OCFIdentificationInfo();
info6.setCarGroup("YYY");
info6.setFrameCode("90");
info6.setFrameSortCode("90");
info6.setLocationIdentificationCode("XXX");
info6.setOcfClassificationCode("HDD navi");


DailyOCF dailyOCF = new DailyOCF();
dailyOCF.setPlanYearMonth("201304");
dailyOCF.setCarSeries("K01");
dailyOCF.setWeekNo("17");
dailyOCF.setDay("");
dailyOCF.setFactoryCode("BBB");
dailyOCF.setLineClass("1");
dailyOCF.setOcfInfo(info);

DailyOCF dailyOCF1 = new DailyOCF();
dailyOCF1.setPlanYearMonth("201304");
dailyOCF1.setCarSeries("K01");
dailyOCF1.setWeekNo("17");
dailyOCF1.setDay("");
dailyOCF1.setFactoryCode("BBB");
dailyOCF1.setLineClass("1");
dailyOCF1.setOcfInfo(info1);

DailyOCF dailyOCF2 = new DailyOCF();
dailyOCF2.setPlanYearMonth("201304");
dailyOCF2.setCarSeries("K01");
dailyOCF2.setWeekNo("17");
dailyOCF2.setDay("");
dailyOCF2.setFactoryCode("BBB");
dailyOCF2.setLineClass("1");
dailyOCF2.setOcfInfo(info2);

DailyOCF dailyOCF3 = new DailyOCF();
dailyOCF3.setPlanYearMonth("201304");
dailyOCF3.setCarSeries("K01");
dailyOCF3.setWeekNo("17");
dailyOCF3.setDay("");
dailyOCF3.setFactoryCode("BBB");
dailyOCF3.setLineClass("1");
dailyOCF3.setOcfInfo(info3);

DailyOCF dailyOCF4 = new DailyOCF();
dailyOCF4.setPlanYearMonth("201304");
dailyOCF4.setCarSeries("K01");
dailyOCF4.setWeekNo("17");
dailyOCF4.setDay("");
dailyOCF4.setFactoryCode("BBB");
dailyOCF4.setLineClass("1");
dailyOCF4.setOcfInfo(info4);


DailyOCF dailyOCF5 = new DailyOCF();
dailyOCF5.setPlanYearMonth("201304");
dailyOCF5.setCarSeries("K01");
dailyOCF5.setWeekNo("17");
dailyOCF5.setDay("");
dailyOCF5.setFactoryCode("BBB");
dailyOCF5.setLineClass("1");
dailyOCF5.setOcfInfo(info5);


DailyOCF dailyOCF6 = new DailyOCF();
dailyOCF6.setPlanYearMonth("201304");
dailyOCF6.setCarSeries("K01");
dailyOCF6.setWeekNo("17");
dailyOCF6.setDay("");
dailyOCF6.setFactoryCode("BBB");
dailyOCF6.setLineClass("1");
dailyOCF6.setOcfInfo(info6);


List<DailyOCF> dailyOCFList = new ArrayList<DailyOCF>();
dailyOCFList.add(dailyOCF);
dailyOCFList.add(dailyOCF1);
dailyOCFList.add(dailyOCF2);
dailyOCFList.add(dailyOCF3);
dailyOCFList.add(dailyOCF4);
dailyOCFList.add(dailyOCF5);
dailyOCFList.add(dailyOCF6);

Ответы [ 2 ]

3 голосов
/ 26 января 2020

Вы можете использовать Collectors.toMap в таком случае:

Map<MyKey, DailyOCF> finalResult = dailyOCFList.stream()
        .collect(Collectors.toMap(ocf -> new MyKey(ocf.planYearMonth, ocf.carSeries, ocf.weekNo, 
                        ocf.factoryCode, ocf.lineClass, ocf.ocfInfo.frameSortCode, 
                        ocf.ocfInfo.ocfClassificationCode, ocf.ocfInfo.locationIdentificationCode, 
                        ocf.ocfInfo.carGroup), Function.identity(), this::mergeDailyOCF));

List<DailyOCF> res = new ArrayList<>(finalResult.values());

, где используемые основные методы будут выглядеть следующим образом:

DailyOCF mergeDailyOCF(DailyOCF dailyOCF1, DailyOCF dailyOCF2) {
    return new DailyOCF(dailyOCF1.planYearMonth, dailyOCF1.carSeries, dailyOCF1.weekNo,
            dailyOCF1.factoryCode, dailyOCF1.lineClass, dailyOCF1.day,
            mergeOCFInfo(dailyOCF1.ocfInfo, dailyOCF2.ocfInfo), Math.max(dailyOCF1.maxQty, dailyOCF2.maxQty),
            Integer.sum(dailyOCF1.actualQty, dailyOCF2.actualQty)); // assign 'actualQty = 1' as default
}

OCFIdentificationInfo mergeOCFInfo(OCFIdentificationInfo info1, OCFIdentificationInfo info2) {
    return info1;
    // implement custom logic if required
    // return new OCFIdentificationInfo(info1.frameSortCode, info1.ocfClassificationCode,
    // info1.locationIdentificationCode, info1.carGroup, info1.frameCode);
}
0 голосов
/ 26 января 2020

Для удобства чтения предположим, что у вас есть метод для создания MyKey из DailyOCF.

private static MyKey myKey( DailyOCF ocf ) {
    return new MyKey(ocf.planYearMonth, ocf.carSeries, ocf.weekNo, ocf.factoryCode,
              ocf.lineClass, ocf.ocfInfo.frameSortCode, ocf.ocfInfo.ocfClassificationCode,
              ocf.ocfInfo.locationIdentificationCode, ocf.ocfInfo.carGroup );
}

Вот то, что я придумал. Если вы хотите агрегировать actualQty или maxQty, конечный результат операции потока будет Map<MyKey, Integer>. Этот код достигнет этого. (Я использовал maxQty в коде здесь, так как он имеет некоторые значения.)

Map<MyKey, Integer> myKeyList = dailyOCFList.stream()
    .collect( Collectors.toMap( Function.identity(), ocf -> myKey( ocf ) ) ).entrySet().stream()
    .collect( Collectors.groupingBy( e -> e.getValue(), Collectors.summingInt( e -> e.getKey().getMaxQty() ) ) );

Если вместо суммирования вам просто нужен сгруппированный список DailyOFC s, то вы можете использовать это :

Map<MyKey, List<DailyOCF>> myKeyList2 = dailyOCFList.stream()
    .collect( Collectors.toMap( Function.identity(), ocf -> myKey( ocf ) ) ).entrySet().stream()
    .collect( Collectors.groupingBy( e -> e.getValue(), Collectors.mapping( e -> e.getKey(), Collectors.toList() ) ) );
...