Salkin
Salkin

Reputation: 41

Grouping arraylist of objects and getting max value per group in java

I have an arraylist called ResultList. This arraylists consists of objects (Results) which in turn have a reference to a Participant object and Competition object as attributes. What I need to do is to group this arraylist (ResultList) by Participant and then put the max result (measurement) for each Participant in a new list.

Result class:

public class Result {

private double measurement;
private int participantID;
private Participant participant;
private Competition competition;

public Result(double measurement, int participantID, Competition competition, Participant participant) {
    this.measurement = measurement;
    this.participant = participant;
    this.competition = competition;
}

Participant class:

public class Participant {

private String firstName;
private String lastName;
private int id;
private String gender;
private String teamName;

public Participant(String firstName, String lastName, int id, String gender, String teamName) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.id = id;
    this.gender = gender;
    this.teamName = teamName;
}

Finally I have the ResultList class:

public class ResultList {

private Scanner sc;

private String listName;
private ArrayList<Result> resultList = new ArrayList<Result>();

public ResultList(Scanner sc, String name) {
    this.sc = sc;
    this.listName = name;
}

In the ResultList class I have a method to sort the list using a comparator, after having iterated through it to find the results for a specific competition:

public void listResultsForCompetition(String competitionName) {
Iterator<Result> it = resultList.iterator();
boolean nothingFound = true;
ArrayList<Result> tempResultList = new ArrayList<Result>();
while (it.hasNext()) {
    Result nextItem = (Result) it.next();
    if (nextItem.getCompetitionName().equalsIgnoreCase(competitionName)) {
        nothingFound = false;
        tempResultList.add(nextItem);
    }
}
    if (nothingFound) {
        System.out.println("Error: No results found for this event. ");
    }
    Collections.sort(tempResultList, Result.ResultComparatorMeasurement);

}

So now I have an arraylist called tempResultList which has all the results for a specific competition ordered by the "measurement" attribute in the Result object. What I need to do now is to somehow get the top result from each participant in the tempResultList, and store this in another list, in order to produce a list of top results by participant for a specific competition.

I've googled for hours but I can't make sense of the answers I've found, as most of them only have a simple list of integers or similarly. I've seen multiple references to maps, stream and collection methods but I can't make sense of it.

EDIT: There is no fixed amount of results or participants in the arraylist.

Upvotes: 3

Views: 2249

Answers (1)

Oleg Cherednik
Oleg Cherednik

Reputation: 18255

In case you have to sort something, List is not a perfect match of the collection. Depending on results, you have to choose best collection.

In your example, I do not see how you want to use List<Result> tempResultList. It seems, that you use is only as temporary collection to get you main objective - get top resulst of all participant. But I can see following things:

First: You already have a List<Result> tempResultList with sorted results (top results at the beginning). All you need is just iterate over this list and for each participant get the first result:

Map<Integer, Result> topResults =  new LinkedHashMap<>();

for(Result result : tempResultList) {
    if(!topResults.containsKey(result.getParticipant().getId()))
        topResults.put(result.getParticipant().getId(), result);
}

Notes: hree I got throug your tempResultList and fill a map, where key is a participant's id and a value - participant's result. If paraticipant is not in a map, then add his result to it (first one in the sorted list will be the top result). You can use here LinkedHashMap to sort keys in map according it apperas in the tempResultList, or TreeMap - to sort it by participant's id. Or even - 'HashMap', when you do not care about an order of topResults.values().

Second: You can build tempResultList and then sort the top rated results at the same time, using Map<Integer, Set<Result>> res. You already has a comparator you need Result.ResultComparatorMeasurement. Key of the map - is a participant's id, value - sorted list of participant's results, with top results first.

public List<Result> listResultsForCompetition(String competitionName) {
    Map<Integer, Set<Result>> participantResults = new HashMap<>();

    for (Result result : resultList) {
        int participantId = result.getParticipant().getId();

        if (!participantResults.containsKey(participantId))

// TreeSet will sort results according to given comparator (top results first) participantResults.put(participantId, new TreeSet<>(Result.ResultComparatorMeasurement));

        participantResults.get(participantId).add(result);
    }

    Map<Integer, Result> topParticipantResults = new HashMap<>();

    for (Map.Entry<Integer, Set<Result>> entry : participantResults.entrySet())
        topParticipantResults.put(entry.getKey(), entry.getValue().iterator().next());

    List<Result> topResults = new ArrayList<>(topParticipantResults.values());

    return topResults;
}

Notes: I do not use stream API here to make it maximum clear. I use TreeSet<Result>(comparator) to receive a sorted set of results (ATTENTION!!! - Set use comparator to fined and exclude duplication, so your comparator should not only compare results, but work correctly where two instance of Result have same measurement). Map<Integer, Set<Result>> participantResults contains all results grouped by participant's id.

There are other way to solve this task. I gave you only 2 ideas how to do it.

Upvotes: 1

Related Questions