Как найти дубликаты объектов по нескольким свойствам и объединить их? - PullRequest
3 голосов
/ 16 октября 2019

Я пишу функцию проверки для нормализации данных между коллекциями в MongoDb. У меня есть объект: как показано ниже:

class ReleaseTime{
  private Date startDate;
  private Date endDate;
  private List<String> regions;
}

Я должен собрать все объекты ReleaseTime, имеющие одинаковые startDate и одинаковыеendDate затем объедините список регионов вместе

Я попробовал приведенный ниже код, но он просто группируется по startDate

expectedAvailabilities = ungrouppedReleaseTime.stream()
            .collect(Collectors.toMap(ReleaseTime::getStartDate,
                    Function.identity(),
                    (ReleaseTime tb1, ReleaseTime tb2) ->
                    {
                        tb1.getRegions().addAll(tb2.getRegions());
                        tb2.getRegions().clear();
                        return tb1;
                    })
            ).values();

Спасибо за вашу помощь!

Ответы [ 2 ]

2 голосов
/ 16 октября 2019

Вот альтернативный способ делать то, что вы хотите, без использования потоков:

Map<List<Date>, List<String>> map = new LinkedHashMap<>();
ungrouppedAvailabilites.forEach(a ->
    map.computeIfAbsent(Arrays.asList(a.getStartDate(), a.getEndDate()), // or List.of
                        k -> new ArrayList<>())
       .addAll(a.getRegions()));

Используется Map.computeIfAbsent для группировки областей ReleaseTime объектов по датам начала и окончания.

Если среди сгруппированных ReleaseTime объектов есть повторяющиеся области и вам не нужны дубликаты, вы можете использовать Set вместо List:

Map<List<Date>, Set<String>> map = new LinkedHashMap<>();
ungrouppedAvailabilites.forEach(a ->
    map.computeIfAbsent(Arrays.asList(a.getStartDate(), a.getEndDate()), // or List.of
                        k -> new LinkedHashSet<>())
       .addAll(a.getRegions()));

Обратите внимание, что я использую LinkedHashMap и LinkedHashSet, чтобы сохранить элементы в порядке вставки.


РЕДАКТИРОВАТЬ:

Если вам нужно ReleaseTime объектоввместо только их регионов вы можете достичь этого с помощью одного дополнительного шага:

Map<List<Date>, ReleaseTime> result = new LinkedHashMap<>();
map.forEach((k, v) -> 
    result.put(k, new ReleaseTime(k.get(0), k.get(1), new ArrayList<>(v))));

Предполагается, что есть конструктор для ReleaseTime, который получает все атрибуты:

public ReleaseTime(Date startDate, Date endDate, List<String> regions) {
    this.startDate = startDate;
    this.endDate = endDate;
    this.regions = regions;
}
2 голосов
/ 16 октября 2019

Вы можете использовать группировку как:

// Collection<ReleaseTime> ungrouppedAvailabilites...
Collection<ReleaseTime> mergedRegionsCollection = ungrouppedAvailabilites.stream()
        .collect(Collectors.toMap(t -> Arrays.asList(t.getStartDate(), t.getEndDate()),
                Function.identity(), ReleaseTime::mergeRegions))
        .values();

, где mergeRegions реализовано как:

ReleaseTime mergeRegions(ReleaseTime that) {
    List<String> mergedRegions = this.getRegions();
    mergedRegions.addAll(that.getRegions());
    return new ReleaseTime(this.startDate, this.endDate, mergedRegions);
}

Примечание. Чтобы избежать изменения существующих объектов, вы можете использоватьреализации как:

ReleaseTime mergeRegions(ReleaseTime that) {
    return new ReleaseTime(this.startDate, this.endDate,
            Stream.concat(this.getRegions().stream(), that.getRegions().stream())
                    .collect(Collectors.toList()));
}
...