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
Android 5.0 - Add Header/Footer to a Recyclerview
Java Using Much More Memory Than Heap Size (Or Size Correctly Docker Memory Limit)
What Is an Efficient Way to Implement a Singleton Pattern in Java
How to Stop Java Process Gracefully
Classpath Does Not Work Under Linux
Java Can't Connect to X11 Window Server Using 'Localhost:10.0' as the Value of the Display Variable
How to Declare and Initialize an Array in Java
How to Convert a String to an Int in Java
How to Deal With Linkageerrors in Java
Java Command Not Found on Linux
Why Use Getters and Setters/Accessors
Sort a Map≪Key, Value≫ by Values
Does a Finally Block Always Get Executed in Java
How to Read the Value of a Private Field from a Different Class in Java
:: (Double Colon) Operator in Java 8
How to Get the Sharedpreferences from a Preferenceactivity in Android