简体   繁体   中英

group a list of triples into map with pair key

I have a list of triples

List<Triple<String, String, String>> triplets; 

I want to group into a map like this

Map<Pair<String, String>, String> mapping;

Where value of the map is the third element of the triple. And in case of the same key it should override the remaining third value.

For example

def triples = [ {a, b, c} ; {a, d, e} ; {a, b, f } ]
// grouping
def map = [ {a,b} : c ; {a, d} : e ]

How to do that using Java 8 and its grouping in streams?

This should do the trick:

Map<Pair<String, String>, String> result = triplets.stream()
    .collect(
        Collectors.toMap(
            t -> new Pair(t.getOne(), t.getTwo()),
            Triple::getThree,
            (v1, v2) -> v2
        )
    );

Example of a partial pair class:

public class Pair<T, U> {
    //...

    @Override
    public int hashCode() {
        return one.hashCode() + two.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Pair))
            return false;
        Pair p = (Pair) obj;
        return p.one.equals(one) && p.two.equals(two);
    }
}

The HashMap class uses equals method to identify key objects uniquely. So you first need to override equals and hashcode methods to show the logical equality of the Pair objects for the Map class.

Then come back to the streams and lambda. For each triplet use Collectors.toMap with the Pair object as the key and the other remaining value of the Triplet as the value. Then provide a mergeFunction to handle key conflicts. In your case, you need to keep the previous value while discarding new value. That's all you need to do.

Update

I have updated the merge function as per the below comments.

If you want to use Collectors.groupingBy , you can combine it with downstream mapping and reducing Collectors :

@Data @AllArgsConstructor
class Triple {
    String x, y ,z;
}
@Data @AllArgsConstructor
class Pair {
    String x, y;
}

List<Triple> lst = List.of(new Triple("a", "b", "c"),
                           new Triple("a", "d", "e"),
                           new Triple("a", "b", "f"));

Map<Pair, String> map = lst.stream()
        .collect(Collectors.groupingBy(t -> new Pair(t.x, t.y),
                 Collectors.mapping(t -> t.z, 
                 Collectors.reducing(null, (x,y) -> y))));
// {Pair(x=a, y=b)=f, Pair(x=a, y=d)=e}

You could also change (x,y) -> y to (x,y) -> x to keep the first occurrance instead.

here ici a possible solution

        String[] l1=new String[]{"a", "b", "c"};
    String[] l2={"a", "d", "e"};

    String[] l3={"a", "b", "c"};
    List<String[]> t=new ArrayList<>();
    t.add(l1);

    t.add(l2);
    t.add(l3);

    Map<String[],String> m=t.stream().
    collect(Collectors.toMap(x->new String[]{x[0],x[1]}, x->x[2],(x,y)->x));

but It depends on the type of Triple if it override equals there will be no duplication otherwise it will be duplication in the key of the map

Using Map::merge :

Map<Pair<String, String>, String> result = new HashMap<>();
triplets.forEach(e -> result.merge(new Pair<>(e.getOne(), e.getThree()), e.getThree(), (o, n) -> o));

This inserts the elements of the triplets list into a map where the keys are a pair representing the first and second value of the Triple and the values of the map are the third value of the Triple .

In the case of a key collision, we use the remapping function (o, n) -> o to maintain the old ( o ) value.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM