简体   繁体   中英

JAVA: How do I merge objects from an arrayList based on a property value?

I'm building (or learning how to) a sports REST API using Spring Boot, Java, and MySQL. I'm building a method that currently takes each match from a collection of matches and returns an ArrayList of TeamStandings for the full list of matches.

Here is the method:

public List<TeamStanding> createStandingsTable(Match[] matches){
        List<TeamStanding> teamStandings = new ArrayList<TeamStanding>();
        for(int i = 0;i < matches.length; i++) {
            TeamStanding firstTeam = new TeamStanding();
            TeamStanding secondTeam = new TeamStanding();

            //set team ids
            firstTeam.setIdTeam(matches[i].getWcmHome());
            secondTeam.setIdTeam(matches[i].getWcmAway());


            //first team stats
            firstTeam.setTeamPlayed((long) 1);
            firstTeam.setTeamGoalsFavor(matches[i].getWcmHomeGoals());
            firstTeam.setTeamGoalsAgainst(matches[i].getWcmAwayGoals());
            firstTeam.setTeamGoalDif(firstTeam.getTeamGoalsFavor() - firstTeam.getTeamGoalsAgainst());

            //second team stats
            secondTeam.setTeamPlayed((long) 1);
            secondTeam.setTeamGoalsFavor(matches[i].getWcmAwayGoals());
            secondTeam.setTeamGoalsAgainst(matches[i].getWcmHomeGoals());
            secondTeam.setTeamGoalDif(secondTeam.getTeamGoalsFavor() - secondTeam.getTeamGoalsAgainst());

            //combined team stats

            if(firstTeam.getTeamGoalsFavor() > secondTeam.getTeamGoalsFavor()) {
                firstTeam.setTeamWins((long) 1);
                firstTeam.setTeamLoses((long) 0);
                firstTeam.setTeamDraws((long) 0);
                firstTeam.setTeamPoints((long) 3);
                secondTeam.setTeamWins((long) 0);
                secondTeam.setTeamLoses((long) 1);
                secondTeam.setTeamDraws((long) 0);
                secondTeam.setTeamPoints((long) 0);
            } else if (firstTeam.getTeamGoalsFavor() == secondTeam.getTeamGoalsFavor()) {
                firstTeam.setTeamWins((long) 0);
                firstTeam.setTeamLoses((long) 0);
                firstTeam.setTeamDraws((long) 1);
                firstTeam.setTeamPoints((long) 1);
                secondTeam.setTeamWins((long) 0);
                secondTeam.setTeamLoses((long) 0);
                secondTeam.setTeamDraws((long) 1);
                secondTeam.setTeamPoints((long) 1);
            } else {
                firstTeam.setTeamWins((long) 0);
                firstTeam.setTeamLoses((long) 1);
                firstTeam.setTeamDraws((long) 0);
                firstTeam.setTeamPoints((long) 0);
                secondTeam.setTeamWins((long) 1);
                secondTeam.setTeamLoses((long) 0);
                secondTeam.setTeamDraws((long) 0);
                secondTeam.setTeamPoints((long) 3);
            }
            teamStandings.add(firstTeam);
            teamStandings.add(secondTeam);
        }
        return teamStandings;
    }

And the result is something like this:

[
    {
        "idTeam": 7,
        "teamPoints": 3,
        "teamPlayed": 1,
        "teamWins": 1,
        "teamDraws": 0,
        "teamLoses": 0,
        "teamGoalsFavor": 4,
        "teamGoalsAgainst": 1,
        "teamGoalDif": 3
    },
    {
        "idTeam": 13,
        "teamPoints": 0,
        "teamPlayed": 1,
        "teamWins": 0,
        "teamDraws": 0,
        "teamLoses": 1,
        "teamGoalsFavor": 1,
        "teamGoalsAgainst": 4,
        "teamGoalDif": -3
    },
    {
        "idTeam": 4,
        "teamPoints": 3,
        "teamPlayed": 1,
        "teamWins": 1,
        "teamDraws": 0,
        "teamLoses": 0,
        "teamGoalsFavor": 1,
        "teamGoalsAgainst": 0,
        "teamGoalDif": 1
    },
    {
        "idTeam": 7,
        "teamPoints": 0,
        "teamPlayed": 1,
        "teamWins": 0,
        "teamDraws": 0,
        "teamLoses": 1,
        "teamGoalsFavor": 0,
        "teamGoalsAgainst": 1,
        "teamGoalDif": -1
    }
]

My question is how can I merge these objects based on the idTeam ? The result I'm trying to achieve would be to have all the rest of the properties added up while the idTeam remains the same. In the given example the expected one would be:

[
        {
            "idTeam": 7,
            "teamPoints": 3,
            "teamPlayed": 2,
            "teamWins": 1,
            "teamDraws": 0,
            "teamLoses": 1,
            "teamGoalsFavor": 4,
            "teamGoalsAgainst": 2,
            "teamGoalDif": 2
        },
        {
            "idTeam": 13,
            "teamPoints": 0,
            "teamPlayed": 1,
            "teamWins": 0,
            "teamDraws": 0,
            "teamLoses": 1,
            "teamGoalsFavor": 1,
            "teamGoalsAgainst": 4,
            "teamGoalDif": -3
        },
        {
            "idTeam": 4,
            "teamPoints": 3,
            "teamPlayed": 1,
            "teamWins": 1,
            "teamDraws": 0,
            "teamLoses": 0,
            "teamGoalsFavor": 1,
            "teamGoalsAgainst": 0,
            "teamGoalDif": 1
        }
    ]

Also just a detail, I built the ArrayList of TeamStandings first and now I'm trying to merge them but perhaps I should be stacking them as a loop through the array of Matches, within the same method above but I'm not sure.

Iterate through the list of TeamStanding , mind the team ID and perform the additions. You might want to use the Map to save the pair of team ID as a key and the team itself as a value for easier manipulation. Here is the snipped (I haven't tested it, so you might need to amend it a bit).

List<TeamStanding> list = createStandingsTable(matches);
Map<Integer, TeamStanding> map = new HashMap<>();

for (TeamStanding team: list) {
    int id = team.getIdTeam();
    if (map.containsKey(id)) {
        TeamStanding other = map.get(id);
        other.setTeamPoints(team.getTeamPoints());
        other.setTeamPlayed(team.getTeamPlayed());
        // and so on...
    } else {
        map.put(id, team);
    }
}

List<TeamStanding> merged = new ArrayList<>(map.values());

If you want to create the merged List<TeamStanding> directly from Match[] , then you have to use the same idea, however, this might be a bit complicated to combine both of the iterations together. Then I recommend you to stick with these two separate iterations. Brevity, readability and maintainability over performance - moreover, the performance is not really an issue here.

You can use HashMap. Use "idTeam" as key and the object TeamStanding as value. Now you can iterate on the result list and if you find the object in the map, just update its fields and if you don't find then insert the object. After iteration finishes, you can call map.values() and it will give you a collection of objects(TeamStanding) and then you can create a new ArrayList with this collection.

The code will go like as follows:

public List<TeamStanding> mergeTeamStandingList(List<TeamStanding> teamStandingList) {
    final Map<Integer, TeamStanding> idTeamVsTeamStandingMap = new HashMap<Integer, TeamStanding>();
    teamStandingList.forEach(teamStanding -> {
        if(idTeamVsTeamStandingMap.containsKey(teamStanding.getIdTeam())) {
            TeamStanding teamStanding1 = idTeamVsTeamStandingMap.get(teamStanding.getIdTeam());
            teamStanding1.setTeamDraws(teamStanding1.getTeamDraws() + teamStanding.getTeamDraws());
            //so on
        } else {
            idTeamVsTeamStandingMap.put(teamStanding.getIdTeam(), teamStanding);
        }
    });

    return new ArrayList<>(idTeamVsTeamStandingMap.values());
}

Create a merge method on your Teamstanding object.

public TeamStanding merge(TeamStanding other) {
     this.teamPoints += other.getTeamPoints();
     this.teamPlayed += other.getTeamPlayed();
     this.teamWins += other.getTeamWins();
     this.teamDraws += other.getTeamDraws();
     this.teamGoalsFavor += other.getTeamGoalsFavor();
     this.teamLoses += other.getTeamLoses();
     this.teamGoalDif += other.getTeamGoalDif();
     return this;
}

Then use Streams to group by teamId and reduce the common items using the merge method.

Map<Integer, Optional<TeamStanding>> mapReduced = teamStandings
.stream()
.collect(groupingBy(TeamStanding::getIdTeam, Collectors.reducing(TeamStanding::merge)));

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