Одним из решений, или, скорее, отправной точкой, было бы
List<Foo> result = list.stream().collect(Collectors.collectingAndThen(
Collectors.groupingBy(
o -> Arrays.asList(o.vacancy_id_1, o.vacancy_id_2),
Collectors.toMap(o -> o.hId, o -> Arrays.asList(o.percent, o.golden))),
m -> m.entrySet().stream().map(e -> new Foo(
e.getKey().get(0), e.getKey().get(1),
e.getValue().values().stream().mapToDouble(l->l.get(1))
.reduce((a,b)->{assert a==b; return a; }).getAsDouble(),
e.getValue().entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, en->en.getValue().get(0)))
)).collect(Collectors.toList())
));
который использует только стандартные классы Collection, что усложняет ситуацию. Он группируется по Arrays.asList(o.vacancy_id_1, o.vacancy_id_2)
, что подразумевает упорядочение идентификаторов. Вы можете обернуть его new HashSet<>(…)
, чтобы получить независимый от порядка ключ, однако это усложняет решение, когда дело доходит до построения экземпляров Foo
, поскольку требуются выделенные id1
и id2
. т.е.
List<Foo> result = list.stream().collect(Collectors.collectingAndThen(
Collectors.groupingBy(
o -> new HashSet<>(Arrays.asList(o.vacancy_id_1, o.vacancy_id_2)),
Collectors.toMap(o -> o.hId, o -> Arrays.asList(o.percent, o.golden))),
m -> m.entrySet().stream().map(e -> {
Iterator<Integer> it = e.getKey().iterator();
return new Foo(
it.next(), it.next(),
e.getValue().values().stream().mapToDouble(l->l.get(1))
.reduce((a,b)->{assert a==b; return a; }).getAsDouble(),
e.getValue().entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, en->en.getValue().get(0)))
);
}).collect(Collectors.toList())
));
Обратите внимание, что new HashSet<>(Arrays.asList(o.vacancy_id_1, o.vacancy_id_2))
можно заменить на Set.of(o.vacancy_id_1, o.vacancy_id_2)
в Java 9.
Выделенный независимый от порядка парный тип упростил бы операцию, особенно когда вы с самого начала заменяете два свойства id одним свойством этого типа как в исходном, так и в результирующем типе.
Еще одним препятствием является «золотая» недвижимость. Без него нижестоящим сборщиком был бы Collectors.toMap(o -> o.hId, o -> o.percent)
, производящий в точности желаемую карту для результата Foo
. Поскольку мы должны перенести сюда еще одно свойство, карта нуждается в последующем шаге преобразования после того, как «золотое» свойство будет уменьшено до одного значения.
Используя парный класс, например
public final class UnorderedPair<T> {
public final T a, b;
public UnorderedPair(T a, T b) {
this.a = a;
this.b = b;
}
public int hashCode() {
return a.hashCode()+b.hashCode()+UnorderedPair.class.hashCode();
}
public boolean equals(Object obj) {
if(this == obj) return true;
if(!(obj instanceof UnorderedPair)) return false;
final UnorderedPair<?> other = (UnorderedPair<?>) obj;
return a.equals(other.a) && b.equals(other.b)
|| a.equals(other.b) && b.equals(other.a);
}
}
и сборщик pairing
из этого ответа, мы получаем
List<Foo> result = list.stream().collect(Collectors.collectingAndThen(
Collectors.groupingBy(
o -> new UnorderedPair<>(o.vacancy_id_1, o.vacancy_id_2),
pairing(
Collectors.toMap(o -> o.hId, o -> o.percent),
Collectors.reducing(null, o -> o.golden,
(a,b) -> {assert a==null || a.doubleValue()==b; return b; }),
(m,golden) -> new AbstractMap.SimpleImmutableEntry<>(m,golden))),
m -> m.entrySet().stream().map(e -> new Foo(
e.getKey().a, e.getKey().b, e.getValue().getValue(), e.getValue().getKey()))
.collect(Collectors.toList())
));
но, как было сказано, наличие одного свойства типа неупорядоченной пары в источнике и результате значительно упростило бы задачу.
11.11.2016
.map(e -> ....
, но код по-прежнему компилируется и работает правильно. 11.11.2016