lookup values from itemgetter in a Dict (python)

萝らか妹 提交于 2019-12-10 11:34:52

问题


I have a list of tuples that I'm trying to sort. The tuples contain strings:

connectionsList = [('C', 'B'), ('A', 'C'), ('D', 'B'), ('C','D')]

The strings in the tuples have numeric values stored in a dict:

valuesDict = {'A':3, 'B':5, 'C':1, 'D':2}

What I'd like to do is sort the list by the sum of the values in the dict for the tuples. Desired output for this toy example would be:

[(C,D), (A,C), (C,B), (D,B)]

which would have the sums of:

[3, 4, 6, 7]

Using itemgetter, I can sort alphabetically by position:

sortedView = sorted(connectionsList, key=itemgetter(0,1))

However, I'm having trouble looking up the values from itemgetter in the dict. Running this:

sortedView = sorted(connectionsList, key=valuesDict[itemgetter(0)] + valuesDict[itemgetter(1)])

gives a dict error of KeyError: operator.itemgetter(0).

How do I create a sort based on the values in the dict?


回答1:


Don't use itemgetter instances; you need to call them but they are overkill here.

Just access the dictionary directly with the key values passed into a lambda. It is a tuple, so add indexing:

sortedView = sorted(connectionsList, key=lambda k: valuesDict[k[0]] + valuesDict[k[1]])

The key argument must be a callable object, that takes a single argument (one of the items being sorted), and returns the value you are actually sorting on. itemgetter() that returns results based on indexing on the object you call it with; itemgetter(0)(some_tuple) would return the first element from the tuple passed in.

A lambda can do the same, in the above solution the lambda returns your desired sum for the given tuple.

Demo:

>>> connectionsList = [('C', 'B'), ('A', 'C'), ('D', 'B'), ('C','D')]
>>> valuesDict = {'A':3, 'B':5, 'C':1, 'D':2}
>>> sorted(connectionsList, key=lambda k: valuesDict[k[0]] + valuesDict[k[1]])
[('C', 'D'), ('A', 'C'), ('C', 'B'), ('D', 'B')]

You could also treat your tuple as an arbitrary-length sequence. If you then also account for the possibility that not all values in the tuples are valid keys for the valuesDict mapping (taking 0 instead), you could also get your solution with:

sortedView = sorted(connectionsList, key=lambda k: sum(valuesDict.get(v, 0) for v in k))

This is more general and robust.

You didn't need to use an itemgetter() object in your alphabetical sort either; the tuples are already provided in 0, 1 order, so you'd get the same sorting order without using a sort key.




回答2:


If you want to look up the keys in the valuesDict and sum them using itemgetter you would have to do something like this:

>>> from operator import itemgetter

>>> connectionsList = [('C', 'B'), ('A', 'C'), ('D', 'B'), ('C','D')]
>>> valuesDict = {'A':3, 'B':5, 'C':1, 'D':2}

>>> idx = 0
>>> sum(itemgetter(*itemgetter(0, 1)(connectionsList[idx]))(valuesDict))
6

The connectionsList[idx] is the current element that would be passed to the key function. So you would need to transform this so that the actual key-function only requires the current element as parameter:

>>> def keyfunc(cur_element):
...     return sum(itemgetter(*itemgetter(0, 1)(cur_element))(valuesDict))
>>> sorted(connectionsList, key=keyfunc)
[('C', 'D'), ('A', 'C'), ('C', 'B'), ('D', 'B')]

However that's not really readable compared to a solution without itemgetter:

>>> def keyfun(cur_element):
...     i1, i2 = cur_element
...     return valuesDict[i1] + valuesDict[i2]
>>> sorted(connectionsList, key=keyfunc)
[('C', 'D'), ('A', 'C'), ('C', 'B'), ('D', 'B')]

So in this case you probably shouldn't use itemgetter because it involves much more function calls and the resulting function isn't that nice.

In case some of the keys might be missing in the valuesDict dictionary, you should replace the [] loopup with get calls:

>>> def keyfun(cur_element):
...     i1, i2 = cur_element
...     return valuesDict.get(i1, 0) + valuesDict.get(i2, 0)
>>> sorted(connectionsList, key=keyfunc)
[('C', 'D'), ('A', 'C'), ('C', 'B'), ('D', 'B')]

Note I generally prefer normal def functions over lambdas when they become more complex, but that's a matter of taste. In this case it's easily possible to translate them to lambdas.




回答3:


You can try something like this:

first collect sum and tuple in a list:

connectionsList = [('C', 'B'), ('A', 'C'), ('D', 'B'), ('C','D')]

valuesDict = {'A':3, 'B':5, 'C':1, 'D':2}


tuple_sum=[]
for i in connectionsList:
    tuple_sum.append((valuesDict.get(i[0])+valuesDict.get(i[1]),i))

it will give:

[(6, ('C', 'B')), (4, ('A', 'C')), (7, ('D', 'B')), (3, ('C', 'D'))]

Now sort it and take only nested tuple :

print(list(map(lambda x:x[1],sorted(tuple_sum))))

output:

[('C', 'D'), ('A', 'C'), ('C', 'B'), ('D', 'B')]


来源:https://stackoverflow.com/questions/48023356/lookup-values-from-itemgetter-in-a-dict-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!