问题
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 lambda
s when they become more complex, but that's a matter of taste. In this case it's easily possible to translate them to lambda
s.
回答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