Find first sequence item that matches a criterion
If you don't have any other indexes or sorted information for your objects, then you will have to iterate until such an object is found:
next(obj for obj in objs if obj.val == 5)
This is however faster than a complete list comprehension. Compare these two:
[i for i in xrange(100000) if i == 1000][0]
next(i for i in xrange(100000) if i == 1000)
The first one needs 5.75ms, the second one 58.3µs (100 times faster because the loop 100 times shorter).
Get the first item from an iterable that matches a condition
Python 2.6+ and Python 3:
If you want StopIteration
to be raised if no matching element is found:
next(x for x in the_iterable if x > 3)
If you want default_value
(e.g. None
) to be returned instead:
next((x for x in the_iterable if x > 3), default_value)
Note that you need an extra pair of parentheses around the generator expression in this case − they are needed whenever the generator expression isn't the only argument.
I see most answers resolutely ignore the next
built-in and so I assume that for some mysterious reason they're 100% focused on versions 2.5 and older -- without mentioning the Python-version issue (but then I don't see that mention in the answers that do mention the next
built-in, which is why I thought it necessary to provide an answer myself -- at least the "correct version" issue gets on record this way;-).
Python <= 2.5
The .next()
method of iterators immediately raises StopIteration
if the iterator immediately finishes -- i.e., for your use case, if no item in the iterable satisfies the condition. If you don't care (i.e., you know there must be at least one satisfactory item) then just use .next()
(best on a genexp, line for the next
built-in in Python 2.6 and better).
If you do care, wrapping things in a function as you had first indicated in your Q seems best, and while the function implementation you proposed is just fine, you could alternatively use itertools
, a for...: break
loop, or a genexp, or a try/except StopIteration
as the function's body, as various answers suggested. There's not much added value in any of these alternatives so I'd go for the starkly-simple version you first proposed.
Return the first item in a list matching a condition
next(x for x in lst if matchCondition(x))
should work, but it will raise StopIteration
if none of the elements in the list match. You can suppress that by supplying a second argument to next
:
next((x for x in lst if matchCondition(x)), None)
which will return None
if nothing matches.
Demo:
>>> next(x for x in range(10) if x == 7) #This is a silly way to write 7 ...
7
>>> next(x for x in range(10) if x == 11)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> next((x for x in range(10) if x == 7), None)
7
>>> print next((x for x in range(10) if x == 11), None)
None
Finally, just for completeness, if you want all the items that match in the list, that is what the builtin filter
function is for:
all_matching = filter(matchCondition,lst)
In python2.x, this returns a list, but in python3.x, it returns an iterable object.
How to find first element in a sequence that matches a predicate in Python?
You can combine ifilter
and islice
to get just the first matching element.
>>> list(itertools.islice(itertools.ifilter(lambda n: n % 2 == 0, lst), 1))
[8]
However, I wouldn't consider this anyhow more readable or nicer than the original code you posted. Wrapped in a function it will be much nicer though. And since next
only returns one element there is no need for islice
anymore:
def find(pred, iterable):
return next(itertools.ifilter(pred, iterable), None)
It returns None
if no element was found.
However, you still have the rather slow call of the predicate function every loop. Please consider using a list comprehension or generator expression instead:
>>> next((x for x in lst if x % 2 == 0), None)
8
Fetch first element of stream matching the criteria
This might be what you are looking for:
yourStream
.filter(/* your criteria */)
.findFirst()
.get();
And better, if there's a possibility of matching no element, in which case get()
will throw a NPE. So use:
yourStream
.filter(/* your criteria */)
.findFirst()
.orElse(null); /* You could also create a default object here */
An example:public static void main(String[] args) {
class Stop {
private final String stationName;
private final int passengerCount;
Stop(final String stationName, final int passengerCount) {
this.stationName = stationName;
this.passengerCount = passengerCount;
}
}
List<Stop> stops = new LinkedList<>();
stops.add(new Stop("Station1", 250));
stops.add(new Stop("Station2", 275));
stops.add(new Stop("Station3", 390));
stops.add(new Stop("Station2", 210));
stops.add(new Stop("Station1", 190));
Stop firstStopAtStation1 = stops.stream()
.filter(e -> e.stationName.equals("Station1"))
.findFirst()
.orElse(null);
System.out.printf("At the first stop at Station1 there were %d passengers in the train.", firstStopAtStation1.passengerCount);
}
Output is:
At the first stop at Station1 there were 250 passengers in the train.
Using map reduce etc, how would you find the first item matching a certain criteria in a nested array, and stop once found?
The easiest (though slightly ugly) solution would be to assign the matching item
to an outer variable when found:
let foundNested;
data.some(subarr => (
subarr.some((item) => {
if (myPredicate(item)) {
foundNested = item;
return true;
}
});
});
You might use .reduce
to avoid assigning to an outer variable:
const myPredicate = ({ val }) => val === 5;const data = [ {arr: [{val:6,name:'aaa'},{val:4,name:'bbb'},{val:8,name:'ccc'}]}, {arr: [{val:3,name:'mmm'},{val:5,name:'nnn'},{val:9,name:'ppp'},{val:5,name:'ooo'}]}];
const found = data.reduce((a, { arr }) => ( a || arr.find(myPredicate)), null);console.log(found);
Concise Way of Get Matching Element in List
You're pretty much there. If you use a generator- rather than list-comprehension, you can then pass it to next
, which takes the first item.
try:
x = next(dict_elem for dict_elem in list_above if dict_elem['name'] == reded)
except StopIteration:
print "No match found"
Or
x = next((dict_elem for dict_elem in list_above if dict_elem['name'] == reded), None)
if not x:
print "No match found"
Concise Way of Get Matching Element in List
You're pretty much there. If you use a generator- rather than list-comprehension, you can then pass it to next
, which takes the first item.
try:
x = next(dict_elem for dict_elem in list_above if dict_elem['name'] == reded)
except StopIteration:
print "No match found"
Or
x = next((dict_elem for dict_elem in list_above if dict_elem['name'] == reded), None)
if not x:
print "No match found"
Related Topics
Using Multipartposthandler to Post Form-Data with Python
Python - Rolling Functions for Groupby Object
What Does "Bound Method" Error Mean When I Call a Function
Stop/Start/Pause in Python Matplotlib Animation
How to Get Around Declaring an Unused Variable in a for Loop
What Do I Do When I Need a Self Referential Dictionary
Popen with Conflicting Executable/Path
Search for "Does-Not-Contain" on a Dataframe in Pandas
Merging Two CSV Files Using Python
Convert Variable Name to String
How to Pass a User Defined Argument in Scrapy Spider
What Is the Recommended Way of Allocating Memory for a Typed Memory View
Automating Pydrive Verification Process
How to Join Two Wav Files Using Python
Creating Dynamically Named Variables from User Input