我有以下数据:
uuid id1 id2 hId hName percent golden
1 J K a fetchflow 38% 34%
2 J K b codelibs1 45% 34%
3 J K c codelibs2 97% 34%
10 K L a fetchflow 16% 10%
11 K L b codelibs1 95% 10%
12 K L c codelibs2 12% 10%
13 K M a fetchflow 64% 14%
14 K M b codelibs1 53% 14%
15 K M c codelibs2 48% 14%
想要达到这个目标:
Compare To Golden a b c
J K 34% 38% 45% 97%
K L 10% 16% 95% 12%
K M 14% 64% 53% 48%
注意: Pair(id1, id2) == Pair(id2, id1)
,因此它们可以互换。
我想将它存储在以下java数据结构中:
class Foo {
int id1;
int id2;
double golden;
/*
[a -> 0.38,
b -> 0.45,
c -> 0.97]
*/
Map<Integer, Double> comparisons;
}
我目前有以下代码,但我无法将其映射到我想要的数据结构:
comparisons
.stream()
.collect(
groupingBy(
Function.identity(),
() -> new TreeMap<>(
Comparator.<ComparisonResultSet, Integer>comparing(o -> o.vacancy_id_1).thenComparing(o -> o.vacancy_id_2)
),
collectingAndThen(
reducing((o, o2) -> o), Optional::get
)
));
答案 0 :(得分:4)
一个解决方案,或者更确切地说,起点是
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)
分组,这意味着ID的排序。您可以使用new HashSet<>(…)
来包装它以获取与订单无关的密钥,但是,当涉及构造Foo
实例时,这会使解决方案变得复杂,作为专用id1
和{ {1}}是必需的。即。
id2
请注意,{9}中的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))
。
专用的与订单无关的对类型会使操作更简单,尤其是当您从源头和结果类型中的两个id属性替换为源类型和结果类型时,从一开始就是这样。
另一个障碍是“黄金”财产。如果没有它,下游收集器将为Set.of(o.vacancy_id_1, o.vacancy_id_2)
,从而为Collectors.toMap(o -> o.hId, o -> o.percent)
结果生成所需的映射。由于我们必须在此处携带另一个属性,因此在将“黄金”属性减少为单个值之后,地图需要后续转换步骤。
使用像
这样的配对类Foo
和来自this answer的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
但是,如上所述,在源和结果中具有无序对类型的单个属性将更多地简化任务。
答案 1 :(得分:0)
我考虑到id1和id2和golden是相同的,id1和id2是可以互换的。
这个怎么样:
list.stream().collect(Collectors.collectingAndThen(Collectors.groupingBy(struct -> {
String first = struct.getId1();
String second = struct.getId2();
if (first.compareTo(second) > 0) {
return ImmutableList.of(first, second, struct.getGolden());
}
return ImmutableList.of(second, first, struct.getGolden());
}, Collectors.toMap(Structure::getHId, Structure::getPercentage)),
elem -> elem.entrySet().stream().map(entry -> {
ImmutableList<?> values = entry.getKey();
return new Foo((String) values.get(0), (String) values.get(1), (Integer) values.get(2),
entry.getValue());
}).collect(Collectors.toList())));
那个可互换的钥匙让事情变得有点难看。