Java lambda intersection two list and remove from result

Viewed 2378

I want to use Java lambda expression for an intersection of two lists and then ever with lambda expression I want to delete from the list.

Example: I have

List<Person> first = new ArrayList<Person>(); 
List<Person> second = new ArrayList<Person>(); 

Suppose that both lists have some Person Object. I want put into List temp intersection of two list filtered by name for example:

List<Person> temp = new ArrayList<>();
for (Person one : first) {
     for (Person two : second) {                        
        if(one.getName.equals(two.getName)) {
                        temp.add(two);
         }
     }
}

Then I want to remove some Person from temp using a filter, for example, using the Surname.

for (Person tmp : temp) {
     for (Person one : first) {
        if (one.getSurname.equals(tmp.getSurname)) {
              temp.remove(tmp);
        }
     }
}

I want to use lambda expression, How i can do?

5 Answers

You may do it like so,

Map<String, Set<String>> nameToSurname = second.stream().collect(
        Collectors.groupingBy(Person::getName, 
            Collectors.mapping(Person::getSurname, Collectors.toSet())));
List<Person> temp = first.stream().
    filter(p -> nameToSurname.containsKey(p.getName()))
    .filter(p -> !nameToSurname.get(p.getName()).contains(p.getSurname()))
    .collect(Collectors.toList());

First create a map from mame to all the surnames with that name using the second list. Then iterate over the first list, for each person, check whether there's a value in the map by passing the name as the key. If it does, then check whether the surnames matches that of the current person. If both the criteria are satisfied, then collect it into a container.

Intersection between first and second:

List<Person> intersection = first.stream()
        .filter(p1 -> second.stream().anyMatch(p2 -> p2.getName().equals(p1.getName())))
        .collect(Collectors.toList());

Remove elements from myList based on elements from first:

first.stream()
    .filter(p1 -> myList.stream().anyMatch(p2 -> p1.getSurName().equals(p2.getSurName())))
    .forEach(myList::remove);

Both for-loops might be compressed into two Stream::filter methods:

List<Person> temp = second.stream()
    .filter(s ->  first.stream().anyMatch(f -> f.getName().equals(s.getName())))
    .filter(s -> !first.stream().anyMatch(f -> f.getSurame().equals(s.getSurame())))
    .collect(Collectors.toList());

Lambdas are not always the best solution.

You can use retainAll method from Collection<E>.

fist.retainAll(second);

Here is solution by method reference and not.

second.stream()
     .filter(first.stream.map(Person::getName).collect(toSet())::contains)
     .filter(not(first.stream.map(Person::getSurname).collect(toSet())::contains))
     .collect(toList());

I didn't write the code in IDE. there may be some compiler errors.

Related