Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to remove elements from a list with lambda based on another list

Tags:

I have List of file Paths: .

List<Path> filePaths; //e.g. [src\test\resources\file\15\54\54_exampleFile.pdf]

54 above refers to file ID

I then obtain a Set of String Ids which my application can handle as follows:

Set<String> acceptedIds = connection.getAcceptedIDs(); //e.g. elements [64, 101, 33]

How can I use Java 8 lambdas to filter out all elements in filePaths that do not contain any of the acceptable Ids that are contained in acceptedIds collection Set.

In other words, I would like to retain in filePaths only the paths that have ids which are in acceptedIds set. For example, 54 is not in the above list so is removed.

filePaths.stream().filter(...).collect(Collectors.toList());
like image 995
M06H Avatar asked Sep 01 '15 15:09

M06H


People also ask

How do you remove an element from a list in another list?

To remove an element from a list using the remove() method, specify the value of that element and pass it as an argument to the method. remove() will search the list to find it and remove it.

How do you exclude a value from one list to another in python?

What you need to do if you want to use remove is loop calling l. remove over and over until you get ValueError and at that point break that loop. That would account for the case that there are multiple occurrences of a value in the list. (The better solution is still your first one, though.)


2 Answers

The most efficient way is to extract the ID from the path, then attempt to find it in the Set, making each filter execute in constant time, ie O(1) giving an overall O(n), where n is the number of paths:

filePaths.stream()
  .filter(p -> acceptedIds.contains(p.getParent().getFileName().toString()))
  .collect(Collectors.toList());

If the reverse approach is done, where each acceptedIds is searched for in the path (as in other answers), each filter is O(m*k), where m is the number of acceptedIds and k is the average Path length, giving an overall O(n * m * k), which will perform very poorly for even moderate sizes of collections.

like image 112
Bohemian Avatar answered Sep 21 '22 01:09

Bohemian


You could write:

filePaths.stream()
         .filter(p -> acceptedIds.stream().anyMatch(id -> p.toString().contains(id)))
         .collect(toList());

This filters each path such that at least one of the acceptedIds is contained in the string representation of the path. You might want to implement something better than contains here, depending on your use-case (matching the beginning of the filename for example).

anyMatch is an operation that determines if at least one element matches the given predicate.

Note that this answer does not make any assumption about the path to filter out elements. If you can safely say that in each path, the parent directory is named with the id, you should definitely go with @Bohemian answer, for performance reason.

like image 42
Tunaki Avatar answered Sep 22 '22 01:09

Tunaki