views:

272

answers:

2

I have been poking around for a recipe / example to index a list of tuples without taking a modification of the decorate, sort, undecorate approach.

For example:

l=[(a,b,c),(x,c,b),(z,c,b),(z,c,d),(a,d,d),(x,d,c) . . .]

The approach I have been using is to build a dictionary using defaultdict of the second element

from collections import defaultdict

tdict=defaultdict(int)

for myTuple in l:
    tdict[myTuple[1]]+=1

Then I have to build a list consisting of only the second item in the tuple for each item in the list. While there are a number of ways to get there a simple approach is to:

tempList=[myTuple[1] for myTuple in l]

and then generate an index of each item in tdict

indexDict=defaultdict(dict)
for key in tdict:
    indexDict[key]['index']=tempList.index(key)

Clearly this does not seem very Pythonic. I have been trying to find examples or insights thinking that I should be able to use something magical to get the index directly. No such luck so far.

Note, I understand that I can take my approach a little more directly and not generating tdict.

output could be a dictionary with the index

indexDict={'b':{'index':0},'c':{'index':1},'d':{'index':4},. . .}

After learning a lot from Nadia's responses I think the answer is no.

While her response works I think it is more complicated than needed. I would simply

 def build_index(someList):
    indexDict={}
    for item in enumerate(someList):
        if item[1][1] not in indexDict:
           indexDict[item[1][1]]=item[0]
    return indexDict
A: 

If i think this is what you're asking...

l = ['asd', 'asdxzc']
d = {}

for i, x in enumerate(l):
    d[x] = {'index': i}
sharth
Why not use enumerate for that?
Nadia Alramli
Didn't know that existed. Makes it quite a bit simpler.
sharth
Your code simple gets the index value of each unique item in the list, I want to get the index value a little more finely. Nadia's code has a clean answer. I don't think there will be a better solution.
PyNEwbie
Yeah I agree. At the time of the original post, I didn't quite understand what you wanted, and I thought it might just be wanting the indexing of a for loop.
sharth
+5  A: 

This will generate the result you want

dict((myTuple[1], index) for index, myTuple in enumerate(l))

>>> l = [(1, 2, 3), (4, 5, 6), (1, 4, 6)]
>>> dict((myTuple[1], index) for index, myTuple in enumerate(l))
{2: 0, 4: 2, 5: 1}

And if you insist on using a dictionary to represent the index:

dict((myTuple[1], {'index': index}) for index, myTuple in enumerate(l))

The result will be:

{2: {'index': 0}, 4: {'index': 2}, 5: {'index': 1}}


EDIT If you want to handle key collision then you'll have to extend the solution like this:

def build_index(l):
    indexes = [(myTuple[1], index) for index, myTuple in enumerate(l)]
    d = {}
    for e, index in indexes:
        d[e] = min(index, d.get(e, index))
    return d

>>> l = [(1, 2, 3), (4, 5, 6), (1, 4, 6), (2, 4, 6)]
>>> build_index(l)
{2: 0, 4: 2, 5: 1}


EDIT 2

And a more generalized and compact solution (in a similar definition to sorted)

def index(l, key):
    d = {}
    for index, myTuple in enumerate(l):
        d[key(myTuple)] = min(index, d.get(key(myTuple), index))
    return d

>>> index(l, lambda a: a[1])
{2: 0, 4: 2, 5: 1}

So the answer to your question is yes: There is a way in Python to index a list of containers (tuples, lists, dictionaries) by an element of a container without preprocessing. But your request of storing the result in a dictionary makes it impossible to be a one liner. But there is no preprocessing here. The list is iterated only once.

Nadia Alramli
This is slick. I see is is a matter of right thinking about the problem. I see that this should generalize to any sub-container in a list of containers. Cool and thanks
PyNEwbie
Actually Nadia this is close but not quite there. Try to add one more tuple with a common middle value, say add (2,4,6) and you will find that you get the index is the enumeration for the last value-as you write to the dictionary you overwrite the values. But it gave me enough to get there I think. I understand your code and since I am really looking to create a dictionary of key begin: value end:value I am about there.
PyNEwbie
I extended the solution to solve your problem
Nadia Alramli
I learn a lot from you, your answer helped me think through and better understand list comprehensions and dictionaries. Your answer though suggests that the answer to my question is no. It is not possible to index a list consisting of some container objects without first accessing and manipulating the container that you want to index from.
PyNEwbie
@PyNEwbie, I didn't suggest that the answer is no. unless your question is 'Is there a one builtin function in python to do it?'. There is a very simple O(n) solution to do it.
Nadia Alramli