How to filter TreeMap<MyKey, Map<Key2,List<Kids>>> ,

Viewed 135

FundingRank and SourceBucket are enums but essentially int

enum FundingRank {
    LESS_THAN_0(0),
    LESS_THAN_1(1),
    LESS_THAN_100(100);

    private final int rank;
    FundingRank(int i) {
        this.rank = i;
    }
}

enum ScoreBucket {
    MORE_THAN_1000(1000),
    MORE_THAN_500(1000),
    MORE_THAN_100(100),
    MORE_THAN_10(10);

    private final int bucket;
    ScoreBucket(int i) {
        this.bucket = i;
    }
}

I have a TreeMap<FundingRank, TreeMap<scoreBucket, List<Kids>> > holding data like

fundingRank  scoreBucket List<Kids>
0              1000       List(with 1 elements) 
                500       List(with 2 elements)
                100       List(with 4 elements)
                 10       List(with 3 elements)
1             1000        List(with 4 elements)
                500       List(with 7 elements)
                100       List(with 4 elements)
                 10       List(with 3 elements)

Now I , want to find element minimum fundingRank and maximum scoreBucket with minimum 5 kids, if not then minimum fundingRank and maximum scoreBucket

 myMap.entrySet().stream()
     .filter(e -> e.getvalue().entryset().stream()
         .filter(e2 ->e2.getvalue().size()).findfirst();


Output sorted map like this:

1 500  List(with 7 elements)
0 100  List(with 4 elements)
1 1000 List(with 4 elements)
1 100  List(with 4 elements)
0 10   List(with 3 elements)
...

So, I am trying to sort the TreeMap<Int, TreeMap<Int, List>>, the sort order is defined as MinFunding rank and max ScoreBucket (with threshold 5, if bucket has 5 kids, meaning if there is any other smaller bucket with more kids, the higher score bucket wins, or if there is a bigger FundingRank with higher bucket size and more kids, still the lower fundingBucket row wins if it meets 5 kids threshold), then pick up the 1st row hope it is clear now

1 Answers

If you want to find the [fundingRank, scoreBucket, list_Kids] triplet with min FundingRank and max ScoreBucket, but only considering triplets with more than 5 kids, you could stream the entries of your outer and inner maps and use a custom Comparator to find it:

Optional<Map.Entry<FundingRank, Map.Entry<ScoreBucket, Integer>>> triplet = 
    outerMap.entrySet().stream()
        .flatMap(outerEntry -> outerEntry.getValue().entrySet().stream()
            .map(innerEntry -> Map.entry(
                 outerEntry.getKey(), 
                 Map.entry(innerEntry.getKey(), innerEntry.getValue().size()))))
        .min(Comparator.comparingInt(t -> t.getValue().getValue() >= 5 ? 0 : 1)
            .thenComparing(t -> t.getKey())
            .thenComparing(t -> t.getValue().getKey(), Comparator.reverseOrder())
            .thenComparing(t -> t.getValue().getValue(), Comparator.reverseOrder()));

triplet.ifPresent(it -> /* do something with the triplet */);

This creates triplets as Map.Entry<FundingRank, Map.Entry<ScoreBucket, Integer>> and finds the min triplet, according to the given Comparator, which (I hope) fulfils your requirements.

We're using a Comparator in Stream.min that first sorts by either 0 or 1, which is a flag that specifies whether there are more than 5 kids or not. This flag is 0 if there are more than 5 kids, so that triplets with more than 5 kids appear first. Then, the comparator sorts by FundingRank ascending, then by ScoreBucket descending and, finally, if there is a tie, it's broken by the actual number of kids descending (just in case).

Here I've used the Java 9+'s Map.entry(...) method to create entries. If you're on Java 8, you should create entries with new AbstractMap.SimpleEntry(...).

Related