Home > Software design >  Remove objects having Identiaclal name & age properties Only if another property is set to False
Remove objects having Identiaclal name & age properties Only if another property is set to False

Time:08-16

I have a list of Person objects and I want to remove similar (i.e. having the same `objects from it.

public class Person {
    private String name;
    private int age;
    private boolean isSelfEmployed;

    // getters, constructor, etc.
}

Sample data:

List<Person> personList = new ArrayList<>();
personList.add(new Person("John", 26, true));    // Include
personList.add(new Person("Erica", 29, false));  // Include
personList.add(new Person("John", 26, false));   // Exclude

For Person objects having the same name and age I want to remove only those objects where isSelfEmployed property is false (i.e. the final list should contain only the first and the second objects).

I'm wonder is it possible to achieve using streams?

CodePudding user response:

We can group Person objects by name and age using collector groupingBy() that would give a Map with each value represented by a list of namesake people have the same and age.

Then we need to iterate over the values of the map, applying the logic for eliminating people having property isSelfEmployed set to false if there's more than one person mapped to the same key.

I assume that we can't rely on the implementation of the equals/hashCode (it might be implemented differently than based on two fields we're interested in) and in order to group Person objects I would use a Java 16 record as a key (we can also make use of a plain class for that, the option with record is less verbose).

And to implement filtering logic I would use mapMulty() operation, which is meant for one-to-many transformations similarly to flatMap and replaces a stream element with zero or more elements of a different type. This operation is also one of the features of Java 16.

List<Person> personList = List.of(
    new Person("John", 26, true),
    new Person("Erica", 29, false),
    new Person("John", 26, false)
);
        
List<Person> result = personList.stream()
    .collect(Collectors.groupingBy(
        person -> {
            record Key(String name, int age) {}
            return new Key(person.getName(), person.getAge());
        }))
    .values().stream()
    .<Person>mapMulti((list, consumer) -> {
        if (list.size() == 1) list.forEach(consumer);
        else list.stream().filter(Person::isSelfEmployed).forEach(consumer);
    })
    .toList();
        
result.forEach(System.out::println);

Output:

Person{name='Erica', age=29, isSelfEmployed=false}
Person{name='John', age=26, isSelfEmployed=true}

A link to Online Demo

CodePudding user response:

I guess this is one of those questions which shouldn't be solved using the Stream API.

While Alexander Ivanchenko's answer provides a way to achieve this by using Stream, here is an example with normal loops:

Map<String, Person> map = new HashMap<>();
for (Person p: personList) {
    // name#age as key
    // assuming `#` will not appear at the end of name or at the beginning of age
    // Using `#` because what is name is John2 and age is 25?
    String k = p.name   "#"   p.age;
    if (map.containsKey(k) && !p.isSelfEmployed) {
        continue;
    }
    map.put(k, p);
}

A simple, recommended, another approach would be to override equals and hashcode of Person class and simply add the List to a Set and the overridden methods will determine the uniqueness in the Set.

  • Related