How to Filter a Java Collection (Based on Predicate)

How to filter a Java Collection (based on predicate)?

Java 8 (2014) solves this problem using streams and lambdas in one line of code:

List<Person> beerDrinkers = persons.stream()
.filter(p -> p.getAge() > 16).collect(Collectors.toList());

Here's a tutorial.

Use Collection#removeIf to modify the collection in place. (Notice: In this case, the predicate will remove objects who satisfy the predicate):

persons.removeIf(p -> p.getAge() <= 16);

lambdaj allows filtering collections without writing loops or inner classes:

List<Person> beerDrinkers = select(persons, having(on(Person.class).getAge(),
greaterThan(16)));

Can you imagine something more readable?

Disclaimer: I am a contributor on lambdaj

How to filter only specific elements by Java 8 Predicate?

Try something like this:

List<Foo> l = list.stream()
.filter(i -> i.getType().equals(TypeEnum.B) ? i.getAmount() > 0 : true)
.collect(Collectors.<Foo>toList());

It checks if i.getAmount() > 0 only if type is equal to TypeEnum.B.

In your previous attempt your predicate was true only if type was TypeEnum.B and amount was greater than 0 - that's why you got only TypeEnum.B in return.

EDIT: you can also check a suggestion made by Holger (share some credits with him) in the comments section and use even shorter version of the expression:

!i.getType().equals(TypeEnum.B) || i.getAmount()>0

Generic collection filtering using predicates

You can pass into the constructor a parameter that specifies the property as a function.

abstract class BaseFirestoreAdapter<T : BaseFirestoreAdapter.DataInterface, VH : RecyclerView.ViewHolder>(val filteredProperty: (T) -> CharSequence) : RecyclerView.Adapter<VH>(), Filterable
{

var sourceList: MutableList<T> = ArrayList()

// ...

override fun performFiltering(keyword: CharSequence): FilterResults {

val keywordRegex = keyword.toString().toRegex(setOf(RegexOption.IGNORE_CASE, RegexOption.LITERAL))

filteredList = sourceList.filter {
keywordRegex.containsMatchIn(Normalizer.normalize(filteredProperty(it), Normalizer.Form.NFD).replace("[^\\p{ASCII}]".toRegex(RegexOption.IGNORE_CASE), ""))
}

results.values = filteredList.sortedWith(orderComparator)
results.count = filteredList.size
}

...
}

The changes I made to yours were adding the constructor parameter filteredProperty, Changing the sourceList type to T, and replacing it.name with filteredProperty(it).

So subclasses will have to call this super-constructor, passing the property in like this:

data class SomeData(val comments: String)

class SomeDataAdapter: BaseFirestoreAdapter<SomeData>(SomeData::comments) {
//...
}

Or if you want to keep it generic:

class SomeDataAdapter(filteredProperty: (T) -> CharSequence): BaseFirestoreAdapter<SomeData>(filteredProperty) //...

How to filter list using Predicate

I'm assuming the Predicate here is from Guava? If so, you could use Iterables.filter:

Iterable<String> filtered = Iterables.filter(original, predicate);

Then build a list from that if you wanted:

List<String> filteredCopy = Lists.newArrayList(filtered);

... but I'd only suggest copying it to another list if you actually want it as a list. If you're just going to iterate over it (and only once), stick to the iterable.

How can I filter directly a collection based on a value?

use a stream and filter:

salesList.stream()
.filter(sale -> sale.getMap().equals(mapName)) // I've changed map to mapName as I am assuming that was a mistake
.collect(Collectors.toCollection(HashSet::new));

This retains elements which satisfy the provided predicate sale -> sale.getMap().equals(mapName).

Note the above does not modify the source, if you want to modify the source then proceed with removeIf:

salesList.removeIf(sale -> !sale.getMap().equals(mapName));

This removes the elements which satisfy the provided predicate sale -> !sale.getMap().equals(mapName).

Java Streams: Find first for multiple filter predicates

How about doing it the other way around? Streaming the products, and applying predicates on them.

List<Predicate<Product>> predicates = getPredicates();
List<Product> products = getProducts();
List<Product> filtered = products.stream().filter(product -> {
Iterator<Predicate<Product>> iterator = predicates.iterator();
while (iterator.hasNext()) {
Predicate<Product> currentPredicate = iterator.next();
if (currentPredicate.test(product)) {
iterator.remove();
return true;
}
}
return false;
}).collect(Collectors.toList());

The downside is you have to be careful which collection you use for predicates, Iterator.remove is not always supported.

Edit: Looks like i wasn't reading carefully enough. I think getting one of each would be best with loop.

List<Product> products = getProducts();
List<Predicate<Product>> predicates = getPredicates();
List<Product> matchingProducts = new ArrayList<>(predicates.size());
for (Product product : products) {
if (predicates.isEmpty()) {
break;
}
for (int predicateIndex = 0; predicateIndex < predicates.size(); predicateIndex++) {
Predicate<Product> predicate = predicates.get(predicateIndex);
if (predicate.test(product)) {
matchingProducts.add(product);
predicates.remove(predicateIndex);
break;
}
}
}

Actually managed to achieve it with a stream and takeWhile, you were correct, Benjamin.

List<Predicate<Product>> predicates = getPredicates();
List<Product> products = getProducts();
List<Product> matches = products.stream()
.takeWhile(product -> !predicates.isEmpty())
.filter(product -> {
Iterator<Predicate<Product>> iterator = predicates.iterator();
while (iterator.hasNext()) {
if (iterator.next().test(product)) {
iterator.remove();
return true;
}
}
return false;
})
.collect(Collectors.toList());

Just make sure takeWhile is before filter, otherwise last matching element gets skipped.

Filtering objects from collection based on user choice/input

For each criterion, you could have a BiPredicate<String, Message>1 which takes a raw input from the user and a message, and tells if the message matches the filtering option2.

Map<String, BiPredicate<String, Message>> criteria = Map.of(
"title", (userTitle, message) -> input.equals(message.getTitle())
...
);

I'll give you a simplified example of how that map can be used:

Scanner scanner = new Scanner(System.in);

String filteringOption = scanner.nextLine();
String userInput = scanner.nextLine();

BiPredicate<String, Message> predicate = criteria.get(filteringOption);

// get all messages from the storage
getAll()
// make a stream out of them
.stream()
// apply the filtering rule from the map
.filter(m -> predicate.test(userInput, m))
// collect them into a list to display
.collect(Collectors.toList());

Later, those predicates may be combined by the logical operations like or(), and() to form a custom filter option. A user's option can be added to the map for consequent calls or calculated on the fly each time the user requests it, like

BiPredicate<String, Message> titleAndDateFilter = 
criteria.get("title").and(criteria.get("date"));

1 You could use a Predicate<Message>, but it would make these functions less isolated since you need to compare a message's context to the given input.

2 I used Java 9's Map.of.

Easy way to filter elements from a collection in Java?

You could iterate through the list using a ListIterator, which has a remove method.

Btw you should declare your list as List<Car> - program for interfaces, not implementation.

How to filter a collection using a list of predicates

The simplest solution I can think of is using all on the predicates list in your filter call like this:

myList.filter { elt -> myPredicates.all { it(elt) } }

The part myPredicates.all { it(elt) } returns true when all predicates are true for the given element elt.



Related Topics



Leave a reply



Submit