-3

I'm trying to sort the values of a dictionary in ascending order by the key, but the problem is that these values have two items in the key and I'm only trying to sort it by the second key item. If that makes any sense.

For example:

{[keyitem1],[keyitem2]:[Value]}

I'm trying to sort it by keyitem2

I tried sorting it by sorting it by their keys

Attempt #1

order = dict(sorted(mylist.keys()))

#1 outputs a ValueError:

ValueError: dictionary update sequence element #0 has length 54; 2 is required

Attempt #2

order = sorted(my_dict, key=operator.itemgetter(1))

#2 doesn't actually sort it, it's just randomised

Expected output

my_dict = {"('first', [0.011])": [1], "('second', [0.012])": [2], "('third', [0.013])": [3], "('fourth', [0.014])": [4], }  

Actual output

my_dict = {"('second', [0.012])": [2], "('fourth', [0.014])": [4], "('first', [0.011])": [1], "('third', [0.013])": [3]}
AwayWeGo
  • 19
  • 3
  • What Python version are you using? – Dani Mesejo Jan 10 '19 at 20:34
  • 1
    The `ValueError` is completely unrelated, because you are creating a new `dict()` from a (sorted) list of keys and no values. You are mixing different problems there. – Martijn Pieters Jan 10 '19 at 20:35
  • 1
    Attempt 2 definitely sorts, the results are not randomised, but your understanding of what item `1` is and what actually happens are not aligned. You are indexing into a **string**, not a tuple, your keys don't have only two elements, they have separate characters, where `(` and `,` and `)` are just some of those characters. – Martijn Pieters Jan 10 '19 at 20:37
  • 1
    How did you create those keys in the first place? You have used `str()` somewhere you shouldn't. You once did have tuples, but you no longer have tuples in your keys, only strings with a value that happens to be a Python expression, if only it where executed. You need to solve that problem first, before you can meaningfully sort anything here – Martijn Pieters Jan 10 '19 at 20:39
  • Depending on which version of Python you have, dictionaries are NOT ordered (reliably at least). As of Python 3.7, dictionaries are ordered by _insertion order_. This behavior can be obtained using `OrderedDict`'s in other versions. See [this](https://stackoverflow.com/questions/39980323/are-dictionaries-ordered-in-python-3-6) for more detail. – busybear Jan 10 '19 at 21:00

2 Answers2

0

Your entries are not in the form of {[keyitem1],[keyitem2]:[Value]}. You can't do that with dictionaries. What you have here is {string: [value]}.

Dictionaries in Python can't be sorted, as their items have no "order". You could however store your data in a list, namely in this case a list of tuples:

>>> my_dict = {"('first', [0.011])": [1], "('second', [0.012])": [2], "('third', [0.013])": [3], "('fourth', [0.014])": [4]}
>>> L = [ (my_dict[k], k) for k in my_dict ]
>>> L
[([4], "('fourth', [0.014])"), ([3], "('third', [0.013])"), ([1], "('first', [0.011])"), ([2], "('second', [0.012])")]

Then you can sort the list, which will sort the items based on the first sub-item it sees in each item. Or make a new sorted list without touching the order of the original:

>>> my_sorted = sorted(L)
>>> print(my_sorted)
[([1], "('first', [0.011])"), ([2], "('second', [0.012])"), ([3], "('third', [0.013])"), ([4], "('fourth', [0.014])")]

You could alternatively use key= and __getitem__ to do sorting by value:

>>> sorted(my_dict, key=my_dict.__getitem__)
["('first', [0.011])", "('second', [0.012])", "('third', [0.013])", "('fourth', [0.014])"]

The fact that your strings look like tuples and your numbers are single-celled lists though makes me wonder if there's something else you can simplify in what you're doing. Perhaps what you really want are tuple entries in the form of "(1, 'first', 0.011)"?

Bill M.
  • 1,388
  • 1
  • 8
  • 16
0

Your keys are not 2 item tuples anymore, they are strings of 2-item tuples. You would be unable to sort correctly until you are able to fix the keys. For the sake of answering the question as is:

Use ast.literal_eval to access your tuple element for sorting logic.

my_dict = {"('second', [0.012])": [2], "('fourth', [0.014])": [4], "('first', [0.011])": [1], "('third', [0.013])": [3]}

from ast import literal_eval
order = sorted(my_dict.items(), key = lambda x: literal_eval(x[0])[1]) #x[0] would refer to keys in the tuples created by my_dict.items()

However, dictionaries inherently are considered unordered in python. If you specifically want a dictionary that retains order, use an OrderedDict

from collections import OrderedDict
result = OrderedDict(order)
print(result)
#Output:
OrderedDict([("('first', [0.011])", [1]), 
             ("('second', [0.012])", [2]), 
             ("('third', [0.013])", [3]), 
             ("('fourth', [0.014])", [4])])

Lastly though, a recommendation. Dictionaries can accept tuples as keys just fine, so with all said done and dusted, you really should just be using tuples directly instead of having them converted to strings unnecessarily. if the 2nd element is always going to be a single value, use a tuple or the float for it directly.

my_dict = {"('second', [0.012])": [2], "('fourth', [0.014])": [4], "('first', [0.011])": [1], "('third', [0.013])": [3]}
fixed_dict = {(literal_eval(k)[0],literal_eval(k)[1][0]):v for k,v in my_dict.items()}
print(fixed_dict)
#Output
{('second', 0.012): [2],
 ('fourth', 0.014): [4],
 ('first', 0.011): [1],
 ('third', 0.013): [3]}
Paritosh Singh
  • 6,034
  • 2
  • 14
  • 33