Skip to main content
clarify
Source Link
ggorlen
  • 59.3k
  • 8
  • 119
  • 173

Amazingly, next's fallback value second parameter mentioned in this comment in a duplicate thread hasn't been shown here yet.

The basic .index() works well when you can compare whole objects, but it's common to need to search a list of objects or dicts for a particular item by a certain property, in which case a generator with a condition is the natural choice:

>>> users = [{"id": 2, "name": "foo"}, {"id": 3, "name": "bar"}] >>> target_id = 2 >>> found_user = next(x for x in users if x["id"] == target_id) >>> found_user {'id': 2, 'name': 'foo'} 

This stops at the first matching element and is reasonably succinct.

However, if no matching element wasis found, it raises a StopIteration error is raised, which is a little awkward to deal with. Luckily, next offers a second parameter next(gen, Nonedefault) fallback to provide a more natural, except-free control flow:

>>> found_user = next((x for x in users if x["id"] == target_id), None) >>> if not found_user: ... print("user not found") ... user not found 

This is a bit more verbose, but still fairly readable.

If an index is desired:

>>> found_idx = next((i for i, x in enumerate(users) if x["id"] == 1), None) >>> found_idx None >>> next((i for i, x in enumerate(users) if x["id"] == 3), None) 1 

As this comment points out, it may be best not to return the typical -1 for a missing index, since that's a valid index in Python. Raising is appropriate if None seems odd to return.

These are a bit verbose, but feel free to bury the code in a helper function if you're using it repeatedly, providing an arbitrary predicate.

>>> def find(it, pred): ... return next((x for x in it if pred(x)), None) ... >>> find(users, lambda user: user["id"] == 2) {'id': 2, 'name': 'foo'} >>> print(find(users, lambda user: user["id"] == 42)) None >>> find("foobAr", str.isupper) # works on any iterable! 'A' 

Amazingly, next's fallback value second parameter mentioned in this comment in a duplicate thread hasn't been shown here yet.

The basic .index() works well when you can compare whole objects, but it's common to need to search a list of objects or dicts for a particular item by a certain property, in which case a generator with a condition is the natural choice:

>>> users = [{"id": 2, "name": "foo"}, {"id": 3, "name": "bar"}] >>> target_id = 2 >>> found_user = next(x for x in users if x["id"] == target_id) >>> found_user {'id': 2, 'name': 'foo'} 

This stops at the first matching element and is reasonably succinct.

However, if no matching element was found, it raises a StopIteration error which is a little awkward to deal with. Luckily, next offers a second parameter next(gen, None) fallback to provide a more natural, except-free control flow:

>>> found_user = next((x for x in users if x["id"] == target_id), None) >>> if not found_user: ... print("user not found") ... user not found 

This is a bit more verbose, but still fairly readable.

If an index is desired:

>>> found_idx = next((i for i, x in enumerate(users) if x["id"] == 1), None) >>> found_idx None >>> next((i for i, x in enumerate(users) if x["id"] == 3), None) 1 

As this comment points out, it may be best not to return the typical -1 for a missing index, since that's a valid index in Python. Raising is appropriate if None seems odd to return.

These are a bit verbose, but feel free to bury the code in a helper function if you're using it repeatedly, providing an arbitrary predicate.

>>> def find(it, pred): ... return next((x for x in it if pred(x)), None) ... >>> find(users, lambda user: user["id"] == 2) {'id': 2, 'name': 'foo'} >>> print(find(users, lambda user: user["id"] == 42)) None >>> find("foobAr", str.isupper) # works on any iterable! 'A' 

Amazingly, next's fallback value second parameter mentioned in this comment in a duplicate thread hasn't been shown here yet.

The basic .index() works well when you can compare whole objects, but it's common to need to search a list of objects or dicts for a particular item by a certain property, in which case a generator with a condition is the natural choice:

>>> users = [{"id": 2, "name": "foo"}, {"id": 3, "name": "bar"}] >>> target_id = 2 >>> found_user = next(x for x in users if x["id"] == target_id) >>> found_user {'id': 2, 'name': 'foo'} 

This stops at the first matching element and is reasonably succinct.

However, if no matching element is found, a StopIteration error is raised, which is a little awkward to deal with. Luckily, next offers a second parameter next(gen, default) fallback to provide a more natural, except-free control flow:

>>> found_user = next((x for x in users if x["id"] == target_id), None) >>> if not found_user: ... print("user not found") ... user not found 

This is a bit more verbose, but still fairly readable.

If an index is desired:

>>> found_idx = next((i for i, x in enumerate(users) if x["id"] == 1), None) >>> found_idx None >>> next((i for i, x in enumerate(users) if x["id"] == 3), None) 1 

As this comment points out, it may be best not to return the typical -1 for a missing index, since that's a valid index in Python. Raising is appropriate if None seems odd to return.

These are a bit verbose, but feel free to bury the code in a helper function if you're using it repeatedly, providing an arbitrary predicate.

>>> def find(it, pred): ... return next((x for x in it if pred(x)), None) ... >>> find(users, lambda user: user["id"] == 2) {'id': 2, 'name': 'foo'} >>> print(find(users, lambda user: user["id"] == 42)) None >>> find("foobAr", str.isupper) # works on any iterable! 'A' 
clarify
Source Link
ggorlen
  • 59.3k
  • 8
  • 119
  • 173

Amazingly, nextnext's fallback value second parameter mentioned in this comment in a duplicate thread hasn't been shown here yet.

>>> found_idx = next((i for i, x in enumerate(users) if x["id"] == 1), -1None) >>> found_idx -1None >>> next((i for i, x in enumerate(users) if x["id"] == 3), -1None) 1 

ThisAs this comment points out, it may be best not to return the typical -1 for a missing index, since that's a valid index in Python. Raising is gettingappropriate if None seems odd to bereturn.

These are a bit verbose, but feel free to bury itthe code in a helper function if you're using it repeatedly, providing an arbitrary predicate:.

>>> def find(it, pred): ... return next((x for x in it if pred(x)), None) ... >>> find(users, lambda user: user["id"] == 2) {'id': 2, 'name': 'foo'} >>> print(find(users, lambda user: user["id"] == 42)) None >>> find("foobAr", str.isupper) # works on any iterable! 'A' 

Here's the index version, which also shows how you could pass an index into the predicate if you happen to need that for filtering.

>>> def find_index(it, pred): ... return next((i for i, x in enumerate(it) if pred(x, i)), -1) ... >>> find_index(users, lambda x, _: x["id"] == 2) 0 >>> find_index(users, lambda x, _: x["id"] == 3) 1 >>> find_index(users, lambda x, _: x["id"] == 42) -1 

Amazingly, next's fallback value second parameter mentioned in this comment in a duplicate thread hasn't been shown here yet.

>>> found_idx = next((i for i, x in enumerate(users) if x["id"] == 1), -1) >>> found_idx -1 >>> next((i for i, x in enumerate(users) if x["id"] == 3), -1) 1 

This is getting to be a bit verbose, but feel free to bury it in a helper function if you're using it repeatedly, providing an arbitrary predicate:

>>> def find(it, pred): ... return next((x for x in it if pred(x)), None) ... >>> find(users, lambda user: user["id"] == 2) {'id': 2, 'name': 'foo'} >>> print(find(users, lambda user: user["id"] == 42)) None >>> find("foobAr", str.isupper) # works on any iterable! 'A' 

Here's the index version, which also shows how you could pass an index into the predicate if you happen to need that for filtering.

>>> def find_index(it, pred): ... return next((i for i, x in enumerate(it) if pred(x, i)), -1) ... >>> find_index(users, lambda x, _: x["id"] == 2) 0 >>> find_index(users, lambda x, _: x["id"] == 3) 1 >>> find_index(users, lambda x, _: x["id"] == 42) -1 

Amazingly, next's fallback value second parameter mentioned in this comment in a duplicate thread hasn't been shown here yet.

>>> found_idx = next((i for i, x in enumerate(users) if x["id"] == 1), None) >>> found_idx None >>> next((i for i, x in enumerate(users) if x["id"] == 3), None) 1 

As this comment points out, it may be best not to return the typical -1 for a missing index, since that's a valid index in Python. Raising is appropriate if None seems odd to return.

These are a bit verbose, but feel free to bury the code in a helper function if you're using it repeatedly, providing an arbitrary predicate.

>>> def find(it, pred): ... return next((x for x in it if pred(x)), None) ... >>> find(users, lambda user: user["id"] == 2) {'id': 2, 'name': 'foo'} >>> print(find(users, lambda user: user["id"] == 42)) None >>> find("foobAr", str.isupper) # works on any iterable! 'A' 
clarify
Source Link
ggorlen
  • 59.3k
  • 8
  • 119
  • 173

Amazingly, next's fallback value second parameter mentioned in this comment in a duplicate thread hasn't been shown here yet.

The basic .index() works well when you can compare whole objects, but it's common to need to search a list of objects or dicts for a particular item by a certain property, in which case a generator with a condition is the natural choice:

>>> users = [{"id": 2, "name": "foo"}, {"id": 3, "name": "bar"}] >>> target_id = 2 >>> found_user = next(x for x in users if x["id"] == target_id) >>> found_user {'id': 2, 'name': 'foo'} 

This stops at the first matching element and is reasonably succinct.

However, if no matching element was found, it raises a StopIteration error which is a little awkward to deal with. Luckily, next offers a second parameter next(gen, None) fallback to provide a more natural, except-free control flow:

>>> found_user = next((x for x in users if x["id"] == target_id), None) >>> if not found_user: ... print("user not found") ... user not found 

This is a bit more verbose, but still fairly readable.

If an index is desired:

>>> found_idx = next((i for i, x in enumerate(users) if x["id"] == 1), -1) >>> found_idx -1 >>> next((i for i, x in enumerate(users) if x["id"] == 3), -1) 1 

This is getting to be a bit verbose, but feel free to bury it in a helper function if you're using it repeatedly, providing an arbitrary predicate:

>>> def find(it, pred): ... return next((x for x in it if pred(x)), None) ... >>> find(users, lambda user: user["id"] == 2) {'id': 2, 'name': 'foo'} >>> print(find(users, lambda user: user["id"] == 42)) None >>> find("foobAr", str.isupper) # works on any iterable! 'A' 

Here's the index version, which also shows how you could pass an index into the predicate if you happen to need that for filtering.

>>> def find_index(it, pred): ... return next((i for i, x in enumerate(it) if pred(x, i)), -1) ... >>> find_index(users, lambda x, _: x["id"] == 2) 0 >>> find_index(users, lambda x, _: x["id"] == 3) 1 >>> find_index(users, lambda x, _: x["id"] == 42) -1 

Amazingly, next's fallback value second parameter mentioned in this comment in a duplicate thread hasn't been shown here yet.

The basic .index() works well when you can compare whole objects, but it's common to need to search a list of objects or dicts for a particular item by a certain property, in which case a generator with a condition is the natural choice:

>>> users = [{"id": 2, "name": "foo"}, {"id": 3, "name": "bar"}] >>> target_id = 2 >>> found_user = next(x for x in users if x["id"] == target_id) >>> found_user {'id': 2, 'name': 'foo'} 

This stops at the first matching element and is reasonably succinct.

However, if no matching element was found, it raises a StopIteration error which is a little awkward to deal with. Luckily, next offers a second parameter next(gen, None) fallback to provide a more natural, except-free control flow:

>>> found_user = next((x for x in users if x["id"] == target_id), None) >>> if not found_user: ... print("user not found") ... user not found 

This is a bit more verbose, but still fairly readable.

If an index is desired:

>>> found_idx = next((i for i, x in enumerate(users) if x["id"] == 1), -1) >>> found_idx -1 >>> next((i for i, x in enumerate(users) if x["id"] == 3), -1) 1 

This is getting to be a bit verbose, but feel free to bury it in a helper function if you're using it repeatedly, providing an arbitrary predicate:

>>> def find(it, pred): ... return next((x for x in it if pred(x)), None) ... >>> find(users, lambda user: user["id"] == 2) {'id': 2, 'name': 'foo'} >>> print(find(users, lambda user: user["id"] == 42)) None 

Here's the index version, which also shows how you could pass an index into the predicate if you happen to need that for filtering.

>>> def find_index(it, pred): ... return next((i for i, x in enumerate(it) if pred(x, i)), -1) ... >>> find_index(users, lambda x, _: x["id"] == 2) 0 >>> find_index(users, lambda x, _: x["id"] == 3) 1 >>> find_index(users, lambda x, _: x["id"] == 42) -1 

Amazingly, next's fallback value second parameter mentioned in this comment in a duplicate thread hasn't been shown here yet.

The basic .index() works well when you can compare whole objects, but it's common to need to search a list of objects or dicts for a particular item by a certain property, in which case a generator with a condition is the natural choice:

>>> users = [{"id": 2, "name": "foo"}, {"id": 3, "name": "bar"}] >>> target_id = 2 >>> found_user = next(x for x in users if x["id"] == target_id) >>> found_user {'id': 2, 'name': 'foo'} 

This stops at the first matching element and is reasonably succinct.

However, if no matching element was found, it raises a StopIteration error which is a little awkward to deal with. Luckily, next offers a second parameter next(gen, None) fallback to provide a more natural, except-free control flow:

>>> found_user = next((x for x in users if x["id"] == target_id), None) >>> if not found_user: ... print("user not found") ... user not found 

This is a bit more verbose, but still fairly readable.

If an index is desired:

>>> found_idx = next((i for i, x in enumerate(users) if x["id"] == 1), -1) >>> found_idx -1 >>> next((i for i, x in enumerate(users) if x["id"] == 3), -1) 1 

This is getting to be a bit verbose, but feel free to bury it in a helper function if you're using it repeatedly, providing an arbitrary predicate:

>>> def find(it, pred): ... return next((x for x in it if pred(x)), None) ... >>> find(users, lambda user: user["id"] == 2) {'id': 2, 'name': 'foo'} >>> print(find(users, lambda user: user["id"] == 42)) None >>> find("foobAr", str.isupper) # works on any iterable! 'A' 

Here's the index version, which also shows how you could pass an index into the predicate if you happen to need that for filtering.

>>> def find_index(it, pred): ... return next((i for i, x in enumerate(it) if pred(x, i)), -1) ... >>> find_index(users, lambda x, _: x["id"] == 2) 0 >>> find_index(users, lambda x, _: x["id"] == 3) 1 >>> find_index(users, lambda x, _: x["id"] == 42) -1 
clarify
Source Link
ggorlen
  • 59.3k
  • 8
  • 119
  • 173
Loading
Source Link
ggorlen
  • 59.3k
  • 8
  • 119
  • 173
Loading