2

Consider some mapping my_map that defines the order of some keys, and some dictionary my_dict that maps the same keys into some values:

my_map = {'x' : 2, 'y' : 0, 'z' : 1}
my_dict = {'x' : 'foo', 'z' : 'bar', 'y' : 'baz'}

I want to get an ordered list of the values of my_dict using the order defined by my_map. My best approach of getting there is:

inv_map = {v: k for k, v in my_map.items()}
ordered_list = [my_dict[k] for k in [inv_map[d] for d in range(len(my_map))]]

Is there a less clunky way of doing the same?

Bob
  • 428
  • 3
  • 11

5 Answers5

3

You could use the sorted function to order your map by value and then convert it:

[my_dict[k] for k in sorted(my_map, key=lambda key: my_map[key])]

Somewhat cleaner at least!

Let's make sure that it works:

>>> my_map = {'x' : 2, 'y' : 0, 'z' : 1}
>>> my_dict = {'x' : 'foo', 'z' : 'bar', 'y' : 'baz'}
>>> [my_dict[k] for k in sorted(my_map, key=lambda key: my_map[key])]
['baz', 'bar', 'foo']
user2842685
  • 104
  • 3
  • 8
2

You can actually use sorted very efficiently here using dict.get:

[my_dict[k] for k in sorted(my_map, key=my_map.get)]

In action:

>>> my_map = {'x' : 2, 'y' : 0, 'z' : 1}
>>> my_dict = {'x' : 'foo', 'z' : 'bar', 'y' : 'baz'}
>>> [my_dict[k] for k in sorted(my_map, key=my_map.get)]
['baz', 'bar', 'foo']
Jab
  • 26,853
  • 21
  • 75
  • 114
1

Depends on the situation you can init final list and pass items to the needed positions

result = [None] * len(my_dict)

for k, v in my_dict.items():
    result[my_map[k]] = v
dukkee
  • 1,112
  • 1
  • 9
  • 17
1

Still another variation (I think it's different from already presented solutions):

[x[1] for x in sorted(my_dict.items(), key=lambda elem: my_map[elem[0]])]

Testing code:

my_map = {'x' : 2, 'y' : 0, 'z' : 1}
my_dict = {'x' : 'foo', 'z' : 'bar', 'y' : 'baz'}

print(my_dict.items())

sorted_result=[x[1] for x in sorted(my_dict.items(), key=lambda elem: my_map[elem[0]])]

print(sorted_result)

Or a bit differently:

sorted_result=list(zip(*sorted(my_dict.items(), key=lambda elem: my_map[elem[0]])))[1]

I wanted to use zip() to split a list of tuples into 2 lists, but in Python 3 zip() returns iterator (not a list), so (as suggested in Transpose/Unzip Function (inverse of zip)?) I wrapped it in list()

PM 77-1
  • 12,933
  • 21
  • 68
  • 111
0

you can use sorted with the values from my_dict and a key function that sorts them with the values from my_map

ordered_list = sorted(my_dict.values(), key=lambda s:my_map[{v: k for k, v in my_dict.items()}[s]])

if it's sorted the wrong way you can use reverse=True

Hadrian
  • 917
  • 5
  • 10
  • while the code correctly returns ['baz', 'bar', 'foo'] in the example here, it doesn't seem to work in general – Bob Jan 27 '21 at 22:31
  • the only way I can find for it not to work is if a key in `my_dict` isn't in `my_map` – Hadrian Jan 27 '21 at 23:03
  • here's an example ```my_map = {'x' : 2, 'y' : 0, 'z' : 1}``` and ```my_dict = {'x' : 100, 'y' : 100, 'z' : 10}```. The correct order is ```[100, 10, 100]```, but this code gives ```[100, 100, 10]```. I couldn't fully figure out why it fails in this example. Maybe because the values of ```my_dict``` are numeric here? – Bob Jan 27 '21 at 23:18
  • 1
    I think it has to do with the fact that it switches the keys/values of `my_dict`, and because `my_dict` has duplicate values, the new dict can't have duplicate keys – Hadrian Jan 27 '21 at 23:36
  • You are right, the same issue occurs for ```my_dict = {'x' : 'foo', 'y' : 'foo', 'z' : 'bar'}``` – Bob Jan 28 '21 at 01:08