views:

127

answers:

5

This is a follow-up on a previous question of mine regarding searching in lists of lists

I have a list with pairs of values as lists in it.

[['a',5], ['b',3], ['c',2] ]

I know the first element of each pair but I don't know the second (it's the result of a calculation and stored in the list with the first element. I sorted the list in descending order of the calculated item.

I want to know in which position each of the items is, which would normally be: list.index('a') if I didn't have the numbers there. Is there a way to get the index number without knowing the entirety of the element?

Something like: list.index(['a',?]) where ? is a wildcard?

Or should I just create a new list from the ordered one with just the first item in order to get it's index?

+5  A: 
[x[0] for x in list].index('a')

But if you are running this code several times, you might want to save the list of x[0]'s.

Ofri Raviv
If you are running this code several times, you should use Michael Anderson's solution so that you don't have to traverse the entire list every time.
Miles
+2  A: 

A simple, flexible one line version:

[i for (i, item) in enumerate([('a', 1), ('b', 3), ('a', 5)]) if item[0] == 'a']
# returns [0, 2]

[i for (i, item) in enumerate([('a', 1), ('b', 3), ('a', 5)]) if item[0] == 'a'][0]
# returns 0

You could wrap it in a function, replace item[0] == 'a' with a call to an arbitrary predicate (like in my original answer; that's where it's flexible) etc. The second version above will throw an exception if no item is found, so wrap it in try/except; the first one will return an empty list.


If you prefer a separate function for the job, check out the one below... It has the benefit of only consuming as much of your iterable as needed to find an appropriate item.

Original answer:

This will allow you to find the index of the first item possessing any property you can think of, though you'll need to express it as a function (a lambda x: x[0] == 'a' suffices in your example case):

def index_by(pred, iterable):
    i = 0
    found = False
    for item in iterable:
        if pred(item):
            found = True
            break
        i += 1
    return i if found else None

Call like so:

index_by(lambda x: x[0] == 'a', [('b', 1), ('a', 5)]) # returns 1

to obtain the zero-based index of the first item in the iterable which satisfies the given predicate. (In other words, the first item in the iterable passed in as the second argument for which the function passed in as the first argument returns a true value.)

If the iterable does not contain an element satisfying the predicate, None is returned.

Michał Marczyk
Incidentally, I followed the specs from the question, so the above code produces the index / indices of elements matching certain criteria... It's trivial to change it to return the elements themselves, though, or perhaps `(index, element)` pairs.
Michał Marczyk
+1  A: 

You already have the original list from which you know the first elements. List call it orig_list. Get the element's index from the original list and use that index in the new list to retrieve.

>>> orig_list = ['a','b','c']
>>> calc_list = [['a',5], ['b',3], ['c',2] ]

>>> calc_list[orig_list.index('a')]
['a',5]
sharjeel
+4  A: 

There's a similar solution to that of Ofris

D = dict([ [x[1][0],  x[0] ] for x in list(enumerate(L)) ])
D['a']
#returns 0

But another nice feature is that if you tweak this a little you can pull out your original values

D = dict([ [x[1][0],  (x[0], x[1][1] ) ] for x in list(enumerate(L)) ])
D['a'][0]
#returns 0
D['a'][1]
#returns 5
Michael Anderson
+1 for being the only answer so far to use dicts
Miles
+1  A: 

The code below will get the indexes of all items in the list that match the pattern ['a', ?]:

>>> l = [['a',5], ['b',3], ['c',2], ['a',8]]
>>> indexes = [l.index(x) for x in l if x[0] == 'a']
>>> print indexes
[0, 3]
Tendayi Mawushe