简体   繁体   中英

Generic java.util.function.Function for Grouping as per input

I have class Foo which has some field and I want to group them to Map<Object,List<Foo>> by fields as follow:

class Foo {
    private String cassette;
    private List<String> organ; //not able to group on List<String>
    private LocalDate date;
    //getter setter toString
}

enum Group{
    CASSETTE,
    ORGAN,
    DATE
}

I have a switch case for Grouping by java.util.function.Function for Collectors.groupingBy() as follow:

Function<Foo, Object> keyCriteria;

Group groupBy = Group.values()[1];//ordinal
switch (groupBy) {
case CASSETTE:
    keyCriteria = p -> p.getCassette();
    break;
case DATE:
    keyCriteria = p -> p.getDate();
    break;
case ORGAN:
    keyCriteria = p -> p.getOrgan(); //facing problem while grouping with List<String>
    break;
default:
    keyCriteria = p -> p.getCassette();
}

Map<Object, List<Foo>> mapByCriteria = fooList.stream()
        .collect(Collectors.groupingBy(keyCriteria));
System.out.println(mapByCriteria);

Everything works fine except case ORGAN:

Result getting:

{[Lung, Liver]=[Foo [cassette=1A, organ=[Lung, Liver], date=2020-01-13]], [Liver]=[Foo [cassette=2A, organ=[Liver], date=2020-01-15]]}

Expected result:

{Liver=[Foo [cassette=1A, organ=[Lung, Liver], date=2020-01-13], Foo [cassette=2A, organ=[Liver], date=2020-01-15]], Lung=[Foo [cassette=1A, organ=[Lung, Liver], date=2020-01-13]]}

Achieved expected result by work around following:

Map<Object, List<Foo>> collect = fooList.stream()
                .flatMap(f -> f.getOrgan().stream().map(o -> new SimpleEntry<>(o, f))).collect(Collectors
                        .groupingBy(Map.Entry::getKey, Collectors.mapping(Map.Entry::getValue, Collectors.toList())));

I'm looking for a switch case and Generic solution.

You can not handle fundamentally different operations, like a plain mapping and a flattening, with the same code. You have to handle the special case specially:

public static Map<String, List<Foo>> mapByCriteria(List<Foo> fooList, Group criteria) {
    Function<Foo, String> simpleKeyCriteria;
    switch(criteria) {
        case CASSETTE: simpleKeyCriteria = Foo::getCassette; break;
        case DATE: simpleKeyCriteria = p -> p.getDate().toString(); break;
        case ORGAN:
            return fooList.stream()
                .flatMap(foo -> foo.getOrgan().stream()
                    .map(organ -> new AbstractMap.SimpleEntry<>(organ, foo)))
                .collect(Collectors.groupingBy(Map.Entry::getKey,
                    Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
        default:
            throw new AssertionError(criteria);
    }
    return fooList.stream().collect(Collectors.groupingBy(simpleKeyCriteria));
}

In principle, it would possible to share more common operations, by splitting the stream statement into parts, but it would produce even more code while the only common code is fooList.stream() . So in this specific case, this is no win. But for completeness:

public static Map<String, List<Foo>> mapByCriteria(List<Foo> fooList, Group criteria) {
    Stream<Foo> stream = fooList.stream(); // imagine more chained common operations
    Function<Foo, String> simpleKeyCriteria = null;
    Collector<Foo, ?, Map<String, List<Foo>>> collector = null;
    switch(criteria) {
        case CASSETTE: simpleKeyCriteria = Foo::getCassette; break;
        case DATE: simpleKeyCriteria = p -> p.getDate().toString(); break;
        case ORGAN: collector = Collectors.flatMapping(
            foo -> foo.getOrgan().stream()
                .map(organ -> new AbstractMap.SimpleEntry<>(organ, foo)),
            Collectors.groupingBy(Map.Entry::getKey,
                Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
        default:
            throw new AssertionError(criteria);
    }
    if(collector == null) collector = Collectors.groupingBy(simpleKeyCriteria);
    return stream.collect(collector);
}

This bears absolutely no code duplication, but as demonstrated, is not necessarily a win over accepting small code duplication.

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