Home > Net >  Merge Person objects with similar attributes into a single Map Entry
Merge Person objects with similar attributes into a single Map Entry

Time:02-18

Person.java

class Person{
    String name;
    Integer age;
    Integer salary;
    //...getter..setter
}

List of Person objects:

List<Person> tempList = new ArrayList<>(List.of(new Person("John",33,10000), new Person("Peter",21,2000), new Person("John",18,5000), new Person("Peter",31,6000)));

Using java streams, how do I tell my program to 'Find all person with same name and perform SUM operations on their Ages and on their Salaries'

So desired outcome for this would be something like:

"John", 51, 1500
"Peter", 52, 8000

What I've tried:

Map<String,Integer> NameAndSalary= tempList.stream().collect(Collectors.groupingBy(Person::getName,Collectors.summingInt(Person::getSalary) ));

Map<String,Integer> NameAndAge= tempList.stream().collect(Collectors.groupingBy(Person::getName,Collectors.summingInt(Person::getAge) ));

This is good but this produce separate results like:

"John", 51

and

"John", 1500

Is there a shortcut to make it:

"John", 51, 1500

CodePudding user response:

As I mentioned in the comments, Java does not have tuples. The way you create a tuple, is to create a class that resembles one. The Person class already resembles a tuple. So, what I did was to create a Java record (the same as a Java POJO minus boilerplate code) and I collected the persons with the same name and summed up their ages and salaries to create a composite person with the same name. My solution:

public class TupleDemo {
    public static void main(String[] args) {
        List<Person> persons = List.of(
                new Person("John",33,10000),
                new Person("Peter",21,2000),
                new Person("John",18,5000),
                new Person("Peter",31,6000));

        Collection<Person> result = persons.stream().collect(Collectors.groupingBy(p -> p.name(),
                Collectors.collectingAndThen(
                        Collectors.reducing((Person p1, Person p2) -> new Person(p1.name(), p1.age()   p2.age(), p1.salary()   p2.salary())),
                        Optional::get))).values();

        result.stream().forEach(person ->{
            System.out.println("Cumulative person: "   person);
        });

    }

    private static record Person(String name, int age, int salary){
        @Override
        public String toString() {
            return "Name: "   name   ", age: "   age   ", salary: "   salary;
        }
    }
}

This outputs:

Cumulative person: Name: John, age: 51, salary: 15000
Cumulative person: Name: Peter, age: 52, salary: 8000

What the code is doing is using a stream to collect, grouping by name, all the Person objects and will create a new Person composed of the previous match and the current match, where the name attribute is kept, and the ages and salaries are added up. It will continue to do this until the stream is totally consumed. So, to this example, if you were to add a new "Peter" with age of 48 and salary of 12000, it will create a "composite" Peter of age 100 and salary of 20000.

  • Related