按两个属性分组并映射到不同的对象

时间:2016-11-11 09:09:44

标签: java sql lambda java-8 java-stream

我有以下数据:

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
                        )
                ));

2 个答案:

答案 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 answerpublic 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())));

那个可互换的钥匙让事情变得有点难看。