Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort a complex Python dictionary by just one of its values

I am writing a little optimization tool for purchasing stamps at the post office.

In the process I am using a dictionary, which I am sorting according to what I learned in this other "famous" question: Sort a Python dictionary by value

In my case my dictionary is mildly more complex:
- one four-item-tuple to make the key
- and another five-item-tuple to make the data.

The origin of this dictionary is an iteration, where each successful loop is adding one line:

MyDicco[A, B, C, D] = eval, post, number, types, over

This is just a tiny example of a trivial run, trying for 75 cents:
{
(0, 0, 1, 1): (22, 75, 2, 2, 0)
(0, 0, 0, 3): (31, 75, 3, 1, 0)
(0, 0, 2, 0): (2521, 100, 2, 1, 25)
(0, 1, 0, 0): (12511, 200, 1, 1, 125)
(1, 0, 0, 0): (27511, 350, 1, 1, 275)
}

So far I am using this code to sort (is is working):

MyDiccoSorted = sorted(MyDicco.items(), key=operator.itemgetter(1))

I am sorting by my evaluation-score, because the sorting is all about bringing the best solution to the top. The evaluation-score is just one datum out of a five-item-tuple (in the example those are the evaluation-scores: 22, 31, 2521, 12511 and 27511).

As you can see in the example above, it is sorting (as I want it) by the second tuple, index 1. But I had to (grumpily) bring my "evaluation-score" to the front of my second tuple. The code is obviously using the entire second-tuple for the sorting-process, which is heavy and not needed.


Here is my question: How can I please sort more precisely. I do not want to sort by the entire second tuple of my dictionary: I want to target the first item precisely.
And ideally I would like to put this value back to its original position, namely to be the last item in the second tuple - and still sort by it.


I have read-up on and experimented with the syntax of operator.itemgetter() but have not managed to just "grab" the "first item of my second item". https://docs.python.org/3/library/operator.html?highlight=operator.itemgetter#operator.itemgetter

(note: It is permissible to use tuples as keys and values, according to:
https://docs.python.org/3/tutorial/datastructures.html?highlight=dictionary and those are working fine for my project; this question is just about better sorting)


For those who like a little background (you will yell at me that I should use some other method, but I am learning about dictionaries right now (which is one of the purposes of this project)):

This optimization is for developing countries, where often certain values of stamps are not available, or are limited in stock at any given post office. It will later run on Android phones.

We are doing regular mailings (yes, letters). Figuring out the exact postage for each destination with the available values and finding solutions with low stocks of certain values is a not-trivial process, if you consider six different destination-based-postages and hundreds of letters to mail.

There are other modules which help turning the theoretical optimum solution into something that can actually be purchased on any given day, by strategic dialog-guidance...

About my dictionary in this question: I iterate over all reasonable (high enough to make the needed postage and only overpaying up to a fraction of one stamp) combinations of stamp-values.

Then I calculate a "success" value, which is based on the number of stamps needed (priority), the number of types needed (lower priority)(because purchasing different stamps takes extra time at the counter) and a very high penalty for paying-over. So lowest value means highest success.

I collect all reasonable "solutions" in a dictionary where the tuple of needed-stamps serves as the key, and another tuple of some results-data makes up the values. It is mildly over-defined because a human needs to read it at this phase in the project (for debugging).

If you are curious and want to read the example (first line):
The colums are:

  • number of stamps of 350 cents
  • number of stamps of 200 cents
  • number of stamps of 50 cents
  • number of stamps of 25 cents
  • evaluation-score
  • calculated applied postage
  • total number of stamps applied
  • total number of stamp-types
  • over-payment in cents if any

Or in words: (Assuming a postal service is offering existing stamps of 350, 200, 50 and 25 cents), I can apply postage of 75 cents by using 1x 50 cents and 1x 25 cents. This gives me a success-rating of 22 (the best in this list), postage is 75 cents, needing two stamps of two different values and having 0 cents overpayment.

like image 586
Martin Zaske Avatar asked Aug 25 '15 16:08

Martin Zaske


People also ask

Can you sort a dictionary by value?

It is not possible to sort a dictionary, only to get a representation of a dictionary that is sorted. Dictionaries are inherently orderless, but other types, such as lists and tuples, are not. So you need an ordered data type to represent sorted values, which will be a list—probably a list of tuples.

How do you sort nested dictionary by value?

Method #1 : Using OrderedDict() + sorted() This task can be performed using OrderedDict function which converts the dictionary to specific order as mentioned in its arguments manipulated by sorted function in it to sort by the value of key passed.

Can you use sort on a dictionary python?

Introduction. We can sort lists, tuples, strings, and other iterable objects in python since they are all ordered objects. Well, as of python 3.7, dictionaries remember the order of items inserted as well. Thus we are also able to sort dictionaries using python's built-in sorted() function.


2 Answers

You can just use a double index, something like this should work:

MyDiccoSorted = sorted(MyDicco.items(), key=lambda s: s[1][2])

Just set 2 to whatever the index is of the ID in the tuple.

like image 52
Morgan Thrapp Avatar answered Nov 14 '22 23:11

Morgan Thrapp


I find it easier to use lambda expressions than to remember the various operator functions.

Assuming, for the moment, that your eval score is the 3rd item of your value tuple (i.e. (post, number, eval, types, over):

MyDiccoSorted = sorted(MyDicco.items(), key=lamba x:x[1][2])

Alternatively, you can create a named function to do the job:

def myKey(x): return x[1][2]
MyDiccoSorted = sorted(MyDicco.items(), key=myKey)
like image 32
Robᵩ Avatar answered Nov 14 '22 21:11

Robᵩ