Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python Nested lists and iteration

I am new to Python and I did my search but I could not find what I am looking for. I apologise in advance if this question has been asked and if I could not find it due to my lack of not knowing the name of what I am trying to achieve. I will gladly read any document you might suggest.

I have a list of lists. e.g. => [int, 'str']

t = [[0234, 'str_0'],
     [1267, 'str_1'],
     [2445, 'str_2']]

I want to find out if a str exists in index(1) position of one of the lists of list t. I can do this with a function containing 2 for or while loops but what I am seeking is to achieve this, if possible, using one single iteration. I want to learn the shortest function.

for input str('str_3'), I want to get int(2) (index of the list which has str_3 on its own 1st index location) for str_1, I want to get int(0)

and for str_1234 I want to get False as it is not in any of the lists within the list t

As a newbie, I would normally do:

for n in range(len(t)):
    if t[n][1] == 'str_1'
        return n
    return False

What I am seeking to get is, if possible, a better and shorter way of achieving this in one line of a code or just simply to learn if there is a smarter, better or more pythonic way that any one of you who is surely more experienced would recommend.

Thank you

like image 366
Phil Avatar asked Aug 18 '12 15:08

Phil


3 Answers

[n for n, (i, s) in enumerate(t) if s == 'str_3']

Explanation:

>>> t = [[100, 'str_1'], [200, 'str_2'], [300, 'str_3']]

# Use enumerate to get each list item along with its index.
>>> list(enumerate(t))
[(0, [100, 'str_1']), (1, [200, 'str_2']), (2, [300, 'str_3'])]

# Use list comprehension syntax to iterate over the enumeration.
>>> [n for n, (i, s) in enumerate(t)]
[0, 1, 2]

# An if condition can be added right inside the list comprehension.
>>> [n for n, (i, s) in enumerate(t) if s == 'str_3']
[2]
>>> [n for n, (i, s) in enumerate(t) if s == 'str_1234']
[]

This will return all of the matching indices, since there could be more than one.

If you know there will only be one index, may I suggest using a dict instead of a nested list? With a dict you can lookup elements in constant time using very straightforward syntax, rather than having to iterate.

>>> t = {'str_1': 100, 'str_2': 200, 'str_3': 300}

>>> 'str_3' in t
True
>>> t['str_3']
300

>>> 'str_1234' in t
False
>>> t['str_1234']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'str_1234'
like image 162
John Kugelman Avatar answered Sep 25 '22 06:09

John Kugelman


Use the any function with a generator expression:

any(el[1] == 'str_1' for el in t)

What this does is loop over t, and for each el in t we test if the second value in it is equal to str_1, just like your loop.

But it'll only do this until it finds an element for which this is true, then stop, returning True. If it doesn't find such an element, it'll return False instead.

If, instead, you don't just want to test for the presence of str_1 but also want to know where it is located, the most efficient method is to use a loop like you did. You can use enumerate to give you both the index and the value in a sequence:

for n, el in enumerate(t):
    if el[1] == 'str_1'
        return n
    return False
like image 39
Martijn Pieters Avatar answered Sep 23 '22 06:09

Martijn Pieters


The example of a for loop that you give is not the usual way of using for loops in python, because the for loop is not a numerical construct (as it is in most languages), but a way of applying a loop body to each element yielded by iteration of the collection given.

the normal way (using a loop) to find the index would be:

for (n, (integer, string)) in enumerate(t):
    if 'str1' == string:
        return n

Note that in this case, sequence assignment is used to assign the elements of each two-item list to separate variables. Try out the enumerate function yourself in the python shell to find out what it does.

If your list is sorted, you may instead wish to use bisect: http://docs.python.org/library/bisect.html

If you merely want to find out if there is a pair which meets your condition, and you do not care about the index, use any, which stops iteration once it finds a match:

any(string == 'str_1' for integer, string in t)

The expression inside any is a generator expression, which yields True or False for each element in t.

Finally, consider if this is really the most appropriate datastructure to use.

like image 43
Marcin Avatar answered Sep 24 '22 06:09

Marcin