alessmar
alessmar

Reputation: 4727

Java Map: group by key's attribute and max over value

I have an instance of Map<Reference, Double> the challenge is that the key objects may contain a reference to the same object, I need to return a map of the same type of the "input" but grouped by the attribute key and by retaining the max value.

I tried by using groupingBy and maxBy but I'm stuck.

private void run () {
    Map<Reference, Double> vote = new HashMap<>();

    Student s1 = new Student(12L);
    vote.put(new Reference(s1), 66.5);
    vote.put(new Reference(s1), 71.71);
    Student s2 = new Student(44L);
    vote.put(new Reference(s2), 59.75);
    vote.put(new Reference(s2), 64.00);

    // I need to have a Collection of Reference objs related to the max value of the "vote" map
    Collection<Reference> maxVote = vote.entrySet().stream().collect(groupingBy(Map.Entry.<Reference, Double>comparingByKey(new Comparator<Reference>() {
        @Override
        public int compare(Reference r1, Reference r2) {
            return r1.getObjId().compareTo(r2.getObjId());
        }
    }), maxBy(Comparator.comparingDouble(Map.Entry::getValue))));
}

class Reference {
    private final Student student;

    public Reference(Student s) {
        this.student = s;
    }
    public Long getObjId() {
        return this.student.getId();
    }
}

class Student {
    private final  Long id;
    public Student (Long id) {
        this.id = id;
    }

    public Long getId() {
        return id;
    }
}

I have an error in the maxBy argument: Comparator.comparingDouble(Map.Entry::getValue) and I don't know how to fix it. Is there a way to achieve the expected result?

Upvotes: 2

Views: 536

Answers (2)

adarsh
adarsh

Reputation: 1503

Using your approach of groupingBy and maxBy:

Comparator<Entry<Reference, Double>> c = Comparator.comparing(e -> e.getValue());

        Map<Object, Optional<Entry<Reference, Double>>> map = 
                           vote.entrySet().stream()
                           .collect(
                            Collectors.groupingBy
                            (
                            e -> ((Reference) e.getKey()).getObjId(),                             
                            Collectors.maxBy(c)));

           // iterate to get the result (or invoke another stream)
        for (Entry<Object, Optional<Entry<Reference, Double>>> obj : map.entrySet()) {
            System.out.println("Student Id:" + obj.getKey() + ", " + "Max Vote:" + obj.getValue().get().getValue());
        }

Output (For input in your question):

Student Id:12, Max Vote:71.71
Student Id:44, Max Vote:64.0

Upvotes: 0

Eklavya
Eklavya

Reputation: 18430

You can use Collectors.toMap to get the collection of Map.Entry<Reference, Double>

Collection<Map.Entry<Reference, Double>> result = vote.entrySet().stream()
        .collect(Collectors.toMap( a -> a.getKey().getObjId(), Function.identity(),
            BinaryOperator.maxBy(Comparator.comparingDouble(Map.Entry::getValue)))).values();

then stream over again to get List<Reference>

List<Reference> result = vote.entrySet().stream()
        .collect(Collectors.toMap(a -> a.getKey().getObjId(), Function.identity(),
            BinaryOperator.maxBy(Comparator.comparingDouble(Map.Entry::getValue))))
        .values().stream().map(e -> e.getKey()).collect(Collectors.toList());

Upvotes: 1

Related Questions