3

I have a dictionary defined as such:

dict: {'KEY1': Decimal('-6.20000'), 'KEY2': Decimal('-2.58000'), 'KEY3': Decimal('6.80000')}

and I want to have either a list or an OrderedDict of the key/value pairs ordered by absolute value.

I tried:

sorted_dict = sorted(mydict, key=lambda k: abs(mydict[k]), reverse=True)

But this only returns a list of the keys, without the corresponding values, although they do seem to be sorted by the absolute values.

How can I get an OrderedDict or a list of tuples containing both the key and the values, but sorted by the absolute value?

Asclepius
  • 57,944
  • 17
  • 167
  • 143
Vincent L
  • 699
  • 2
  • 11
  • 25
  • `sorted((abs(k), v) for k, v in d.items(), reverse=True)` Is one way – Patrick Haugh Oct 11 '17 at 03:52
  • I get an error: sorted_dict = sorted((abs(k), v) for k, v in dict.items(), reverse=True) ^ SyntaxError: Generator expression must be parenthesized if not sole argument – Vincent L Oct 11 '17 at 04:08
  • And when I change it to sorted_dict = sorted(((abs(k), v) for k, v in dict.items()), reverse=True) I get another error: Trade loop error: (, TypeError("bad operand type for abs(): 'str'",), ) – Vincent L Oct 11 '17 at 04:09
  • My bad, try `sorted((abs(v), k) for k, v in d.items(), reverse=True)` – Patrick Haugh Oct 11 '17 at 04:12
  • So this gives me a list of tuples with items of key and absolute value. What I want is a list that contains the original values(negatives as well), but *sorted* by absolute value. It also gives a list of tuples with the value first and the key second, which is not what is expected – Vincent L Oct 11 '17 at 04:17

2 Answers2

2

You're on the right track. Use .items and pass the resulting tuple pairs into the OrderedDict constructor.

from collections import OrderedDict

values = {
    'KEY1': Decimal('-6.20000'),
    'KEY2': Decimal('-2.58000'),
    'KEY3': Decimal('6.80000')
}

sorted_pairs = sorted(values.items(), key=lambda k: abs(k[1]), reverse=True)
ordered_dict = OrderedDict(sorted_pairs)
Shadow
  • 8,749
  • 4
  • 47
  • 57
  • 1
    Thanks. This is the same answer as PM 2Ring's. I see it was posted at around the same time, so I guess you hadn't seen his yet. I already accepted his answer, but thanks for the input :) – Vincent L Oct 11 '17 at 18:33
  • Technically the timestamps say that mine was posted first by about half an hour... But either way, I'm glad you got your question answered :) – Shadow Jan 14 '21 at 00:18
  • 1
    Oh absolutely, I'm not sure why I missed it! Put yours as the correct answer :) – Vincent L Jan 19 '21 at 03:38
2

You just need a key function that receives a (key, value) tuple from the dictionary .items() View and returns the absolute value of that value. Eg:

from decimal import Decimal
from collections import OrderedDict

data = {'KEY1': Decimal('-6.20000'), 'KEY2': Decimal('-2.58000'), 'KEY3': Decimal('6.80000')} 
out = OrderedDict(sorted(data.items(), key=lambda t: abs(t[1])))
print(out)

output

OrderedDict([('KEY2', Decimal('-2.58000')), ('KEY1', Decimal('-6.20000')), ('KEY3', Decimal('6.80000'))])

It's a little easier to read if we use a proper def function for the key function:

def keyfunc(t):
    return abs(t[1])

out = OrderedDict(sorted(data.items(), key=keyfunc))
PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
  • Works perfect, thanks! At first I thought the OrderedDict conversion messed up the order, but this is simply how the eclipse debugger shows it- it reorders the dict entries by key name... Printing it to the console output shows the correct order. – Vincent L Oct 11 '17 at 04:46
  • @VincentL Ah! Yes, there are a couple of IDEs that "helpfully" sort dictionaries by key when they display them. But you'd think they'd be smart enough to not sort `OrderedDict`s... – PM 2Ring Oct 11 '17 at 05:14
  • @VincentL On a related note, take a look at [Using an OrderedDict in **kwargs](https://stackoverflow.com/questions/26748097/using-an-ordereddict-in-kwargs) – PM 2Ring Oct 11 '17 at 05:16