find first element and index matching condition in list - python

Consider this simple example
mylist = [-1,-2,3,4,5,6]
for idx, el in enumerate(mylist):
if el > 0:
myidx, myel = idx, el
break
myidx, myel
Out[20]: (2, 3)
I am interested in finding the first index and the corresponding first element in a python list that matches a specific condition (here, this is simply > 0).
In the code above, I loop over the elements using enumerate and then use the if clause to find the correct elements. This looks very cumbersome to me. Is there a better way to do this? Using a native python function for instance?
Thanks!

Something like this should work:
l = [-1,-2,3,4,5,6]
list(x > 0 for x in l).index(True)
# Output: 2
To find all patters, we can use python built in functions using
from itertools import filterfalse
f = filterfalse(lambda x: x[1] <= 0, enumerate(l))
print(list(f))
# [(2, 1), (3, 2), (4, 3)]

You could do it in a list comprehension. This is basically the same as your code but condensed into one line, and it builds a list of results that match the criteria.
The first way gets all the matches
mylist = [-1,-2,3,4,5,6]
results = [(i, el) for i, el in enumerate(mylist) if el > 0]
Another way would be to use a generator expression which is probably faster, and just unpack it. This gets the first one.
*next((i, el) for i, el in enumerate(mylist) if el > 0))
This loops the list and checks the condition, then puts the index and element into a tuple. Doing this inside parentheses turns it into a generator, which is much faster because it hasn't actually got to hold everything in memory, it just generates the responses as you need them. Using next() you can iterate through them. As we only use next() once here it just generates the first match. Then we unpack it with *
As there are two other valid answers here I decided to use timeit module to time each of them and post the results. For clarity I also timed the OP's method. Here is what I found:
import timeit
# Method 1 Generator Expression
print(timeit.timeit('next((i, el) for i, el in enumerate([-1,-2,3,4,5,6]) if el > 0)', number=100000))
0.007089499999999999
# Method 2 Getting index of True
print(timeit.timeit('list(x > 0 for x in [-1,-2,3,4,5,6]).index(True)', number=100000))
0.008104599999999997
# Method 3 filter and lambda
print(timeit.timeit('myidx , myel = list(filter(lambda el: el[1] > 0, enumerate([-1,-2,3,4,5,6])))[0]', number=100000))
0.0155314
statement = """
for idx, el in enumerate([-1,-2,3,4,5,6]):
if el > 0:
myidx, myel = idx, el
break
"""
print(timeit.timeit(statement, number=100000))
0.04074070000000002

You can make use of the combination of lambda and filter like this:
mylist = [-1,-2,3,4,5,6]
myidx, myel = list(filter(lambda el: el[1] > 0, enumerate(mylist)))[0]
print("({}, {})".format(myidx, myel))
Explanation:
The filter() function which offers an elegant way to filter out all the elements takes in a function and a list as arguments. Here they are lambda and mylist. Since you want to get the corresponding index, we need to use enumerate to wrap up enumerate(mylist).
Basically, enumerate(mylist) returns a tuple of an index and the corresponding value. Our condition here is the comparison between the value and 0 so that's why we get el[1] instead of el[0] to compare with 0.
The results will be casted to list. This list includes all the pairs (index, value) that meet our condition. Here we want to get the first pair so that's why we have [0] at the end.
Output:
(2, 3)

Related

Exclude an element in a list [duplicate]

This question already has answers here:
Slicing out a specific from a list
(2 answers)
Index all *except* one item in python
(11 answers)
Closed 2 years ago.
Is it possible to use slice but on a specific element on a list? For example a = [1,2,3,4,5,6,7,8,9] I want to make a for loop that prints out the whole list except the second element.
I want to make something like this:
for i in a[something_slice]:
print(i)
Is this possible?
For excluding just one element, the 2 slice lst[:i] + lst[i + 1:] approach proposed by #Applet123 is probably the fastest (Or perhaps a excluded = lst.pop(1) to extract the excluded element and for x in lst: print(x) for printing all the others; then lst.insert(1,excluded) to put the excluded element back on the list. See data structures docs for details).
If you just want to filter out certain indexes, instead of a for loop I recommend you use a more pythonic (and intuitive) approach based on list comprehensions and enumerate:
myList = [1,2,3,4,5,6,7,8,9]
excludedIndices = [1]
myFilteredList = [x for i, x in enumerate(myList) if i not in excludedIndices]
print (myFilteredList)
# output:
# [1,3,4,5,6,7,8,9]
# or, to actually print each element individually:
for x in myFilteredList:
print (x)
# which can also work as a 2-liner with inline filtering:
for i, x in enumerate(myList):
if i not in excludedIndices: print(x)
Also check out python usage of filter and map builtin functions, which may be overkill for this purpose but still offer a general and more powerful solution for this kind of processing:
# filters an enumerated element
def myFilter(element):
return element[0] not in excludedIndices
# maps an enumerated element to a function
def myMap(element):
print(element[1])
# runs myMap function for each enumerated element on the list filtered by myFilter
for x in map(myMap,filter(myFilter,enumerate(myList))): pass
Which you can also turn into a one-liner using lambda expressions:
for x in map(lambda x: print(x[1]),filter(lambda x: x[0] not in excludedIndices,enumerate(myList))): pass
you can do it without slicing, using enumerate()
index_to_skip=1
for idx,item in enumerate(a):
if idx!=index_to_skip:
print(item)
If you actually want to slice the list, you can use 2 slices to slice around it:
def exclude(lst, i):
return lst[:i] + lst[i + 1:]
exclude([1, 2, 3, 4, 5], 1) # [1, 3, 4, 5]
If you just want to loop through it, you could alternatively just skip when the index reaches the value you want to skip:
for i, v in enumerate(a):
if i == 1:
continue
print(v)

How to return the index of the list item

I have the following list in Python:
x = [[1,2],[3,4],[5,'inf'],[6,7],['nan',10]]
How can I return the index of the items that contain 'inf' or 'nan'?
EDIT-1
The first thing I thought about is to make an if-statement, as follows:
if (float('inf') in i) or (float('nan') in i)
But, didn't know how to go about returning the index value.
EDIT-2
The question that has been marked as duplicate didn't actually answer my question.
Thanks.
you can iterate over the list and check for nan or inf:
x = [[1,2],[3,4],[5,'inf'],[6,7],['nan',10]]
for y in x:
if 'inf' in y:
print(x.index(y),y.index('inf'))
elif 'nan' in y:
print(x.index(y), y.index('nan'))
the result will be:
2 1
4 0
I'm glad you added an attempt! We SO users love when folks show their work per say before asking a question. It shows that you are genuinely care about the problem you're trying to solve and not just here for a free-work service.
That being said, I like the simplicity of a list comprehension here:
>>> [(i, j) for i, seq in enumerate(x) for j, item in enumerate(seq) if item == 'inf' or item == 'nan']
[(2, 1), (4, 0)]
A more concise version of the above that takes advantage of sets and their performant membership checks:
>>> [(i, j) for i, seq in enumerate(x) for j, item in enumerate(seq) if item in {'inf', 'nan'}]
What we do here is (1) iterate through the nested list in a nested order, (2) check to see if our item matches the conditions you laid out, (3) capture the index of that position via the result of our enumerate() call.
Suggested Readings:
Python Strings
Enumerate function
[(i, j) for i, outer in enumerate(x) for j, inner in enumerate(outer ) if inner in ['inf','nan']]

list comprehension with iteration

Suppose i have a list L which consists of integer elements. I want to construct a list T which for each index i in 0..len(L) contains the item L[i] provided it is larger than 0 using List comprehension in python.
I tried the following command to do this
T=[L[i] if L[i]>0 for i in range(len(L))]
but i keep getting an error of invalid syntax. How would i do this correctly using List comprehensions in python?
Your syntax is wrong :
L = [1,2,-4,5,-6,7,8,9]
T = [L[i] for i in range(len(L)) if L[i]>0]
Output:
[1, 2, 5, 7, 8, 9]
Also you can just iterate through the elements of the list per se, no need to use a range. Like this : T = [i for i in L if i>0]
Remember :
When only if is there the syntax is [expression for var in list if ...]
When both if and else is there the syntax is [expression1 if ... else expression2 for var in list]
You need to write the filter after the iteration part. So:
T=[L[i] for i in range(len(L)) if L[i]>0]
# \__/ \_______ ____________/ \___ ___/
# yield v v
# iteration filter
Right now Python thinks that you want to write a ternary operator, like:
T=[L[i] if L[i] > 0 else 0 for i in range(len(L))]
This is incorrect: here you would evaluate L[i] if L[i] > 0 else 0 for every element, and you would thus add a 0 for every item L[i] where the element is less than or equal to zero.
That being said, you can write your list comprehension more elegant (and faster), with:
T = [l for l in L if l > 0]
So instead of iterating over indices, we iterate over the elements l in L. We also filter on l and yield l in case the filtering is successful.
While you can use a list comprehension to solve this problem, you can also use filter with a lambda function:
final_l = list(filter(lambda x:x > 0, L))

What does enumerate() mean?

What does for row_number, row in enumerate(cursor): do in Python?
What does enumerate mean in this context?
The enumerate() function adds a counter to an iterable.
So for each element in cursor, a tuple is produced with (counter, element); the for loop binds that to row_number and row, respectively.
Demo:
>>> elements = ('foo', 'bar', 'baz')
>>> for elem in elements:
... print elem
...
foo
bar
baz
>>> for count, elem in enumerate(elements):
... print count, elem
...
0 foo
1 bar
2 baz
By default, enumerate() starts counting at 0 but if you give it a second integer argument, it'll start from that number instead:
>>> for count, elem in enumerate(elements, 42):
... print count, elem
...
42 foo
43 bar
44 baz
If you were to re-implement enumerate() in Python, here are two ways of achieving that; one using itertools.count() to do the counting, the other manually counting in a generator function:
from itertools import count
def enumerate(it, start=0):
# return an iterator that adds a counter to each element of it
return zip(count(start), it)
and
def enumerate(it, start=0):
count = start
for elem in it:
yield (count, elem)
count += 1
The actual implementation in C is closer to the latter, with optimisations to reuse a single tuple object for the common for i, ... unpacking case and using a standard C integer value for the counter until the counter becomes too large to avoid using a Python integer object (which is unbounded).
It's a builtin function that returns an object that can be iterated over. See the documentation.
In short, it loops over the elements of an iterable (like a list), as well as an index number, combined in a tuple:
for item in enumerate(["a", "b", "c"]):
print item
prints
(0, "a")
(1, "b")
(2, "c")
It's helpful if you want to loop over a sequence (or other iterable thing), and also want to have an index counter available. If you want the counter to start from some other value (usually 1), you can give that as second argument to enumerate.
I am reading a book (Effective Python) by Brett Slatkin and he shows another way to iterate over a list and also know the index of the current item in the list but he suggests that it is better not to use it and to use enumerate instead.
I know you asked what enumerate means, but when I understood the following, I also understood how enumerate makes iterating over a list while knowing the index of the current item easier (and more readable).
list_of_letters = ['a', 'b', 'c']
for i in range(len(list_of_letters)):
letter = list_of_letters[i]
print (i, letter)
The output is:
0 a
1 b
2 c
I also used to do something, even sillier before I read about the enumerate function.
i = 0
for n in list_of_letters:
print (i, n)
i += 1
It produces the same output.
But with enumerate I just have to write:
list_of_letters = ['a', 'b', 'c']
for i, letter in enumerate(list_of_letters):
print (i, letter)
As other users have mentioned, enumerate is a generator that adds an incremental index next to each item of an iterable.
So if you have a list say l = ["test_1", "test_2", "test_3"], the list(enumerate(l)) will give you something like this: [(0, 'test_1'), (1, 'test_2'), (2, 'test_3')].
Now, when this is useful? A possible use case is when you want to iterate over items, and you want to skip a specific item that you only know its index in the list but not its value (because its value is not known at the time).
for index, value in enumerate(joint_values):
if index == 3:
continue
# Do something with the other `value`
So your code reads better because you could also do a regular for loop with range but then to access the items you need to index them (i.e., joint_values[i]).
Although another user mentioned an implementation of enumerate using zip, I think a more pure (but slightly more complex) way without using itertools is the following:
def enumerate(l, start=0):
return zip(range(start, len(l) + start), l)
Example:
l = ["test_1", "test_2", "test_3"]
enumerate(l)
enumerate(l, 10)
Output:
[(0, 'test_1'), (1, 'test_2'), (2, 'test_3')]
[(10, 'test_1'), (11, 'test_2'), (12, 'test_3')]
As mentioned in the comments, this approach with range will not work with arbitrary iterables as the original enumerate function does.
The enumerate function works as follows:
doc = """I like movie. But I don't like the cast. The story is very nice"""
doc1 = doc.split('.')
for i in enumerate(doc1):
print(i)
The output is
(0, 'I like movie')
(1, " But I don't like the cast")
(2, ' The story is very nice')
I am assuming that you know how to iterate over elements in some list:
for el in my_list:
# do something
Now sometimes not only you need to iterate over the elements, but also you need the index for each iteration. One way to do it is:
i = 0
for el in my_list:
# do somethings, and use value of "i" somehow
i += 1
However, a nicer way is to user the function "enumerate". What enumerate does is that it receives a list, and it returns a list-like object (an iterable that you can iterate over) but each element of this new list itself contains 2 elements: the index and the value from that original input list:
So if you have
arr = ['a', 'b', 'c']
Then the command
enumerate(arr)
returns something like:
[(0,'a'), (1,'b'), (2,'c')]
Now If you iterate over a list (or an iterable) where each element itself has 2 sub-elements, you can capture both of those sub-elements in the for loop like below:
for index, value in enumerate(arr):
print(index,value)
which would print out the sub-elements of the output of enumerate.
And in general you can basically "unpack" multiple items from list into multiple variables like below:
idx,value = (2,'c')
print(idx)
print(value)
which would print
2
c
This is the kind of assignment happening in each iteration of that loop with enumerate(arr) as iterable.
the enumerate function calculates an elements index and the elements value at the same time. i believe the following code will help explain what is going on.
for i,item in enumerate(initial_config):
print(f'index{i} value{item}')

Returning list elements and indices [duplicate]

This question already has answers here:
'Return' keyword returns only one element from a loop?
(3 answers)
Closed 8 years ago.
I am trying to print all the elements of a list using a user made function.
y = [1,2,3]
def ash(list1):
for x in range(len(list)):
return list[x],x
what i want to do is return all the values in the list with their indices,but all i get is a single element.I can get the elements to print but not return.
You are returning on the very first iteration. Rather, you need to create a list in your function, and add all the tuples in that list, and finally return that list.
And for getting both index and element, you can use enumerate():
def ash(list1):
new_list = []
for x, elem in enumerate(list1):
new_list.append((elem,x))
return new_list
Or, even better you can simply use List comprehension:
return [(elem, x) for x, elem in enumerate(list1)]
The previous two methods creates the list in memory. If you have a very large list to deal with, then you should probably use generators, using yield keyword:
def ash(list1):
for x, elem in enumerate(list1):
yield elem, x
Some issues with your code:
Don't iterate using range unless necessary. Iterate the list directly, or here, use enumerate
Don't use list as a variable - you'll shadow the built-in of the same name. It's confusing to the reader.
You're returning out of the loop. This is why you only get the first iteration. If you want to return successive values, use yield, which turns your function into a generator:
def ash(l):
for x in range(len(l)):
yield l[x],x
This is really a reimplementation of enumerate:
list(enumerate('abc')) #=> [(0, 'a'), (1, 'b'), (2, 'c')]
If you really want to swap the order of the pairs, you can do:
[b,a for a,b in enumerate('abc')]
Alternative implementation: l='abc';zip(l,xrange(len(l)))
enumerate(list) is what you're looking for. (see doc). Also, calling return will give you only the first value of the list when calling the function, what you want here is probably the yield statement:
def ash(list):
for i,item in enumerate(list):
yield item,i
if __name__ == '__main__':
y = [1,2,3]
ys_with_indices = list(ash(y))
print ys_with_indices
Note that this will return a generator object, which you have to convert to a list by calling list() on it. Alternatively, just use a normal list that you append the individual values to:
def ash(list):
items_with_indices = []
for i,item in enumerate(list):
items_with_indices.append((item,i))
return items_with_indices
if __name__ == '__main__':
y = [1,2,3]
ys_with_indices = ash(y)
print ys_with_indices
def ash(lst):
return [(lst[x],x) for x in range(0,len(lst))]
You will get a list of tuples where the first value of the tuple is an element of the original list and the second the index of the element in the list.
For y = [1,2,3] the result is [(1, 0), (2, 1), (3, 2)]

Categories