2

How can I sort a list of python objects by attribute based on a defined mapping

E.g.

import random


class Obj(object):
    attr = None

    def __init__(self, attr):
        self.attr = attr

    def __repr__(self):
        return self.attr

attrs = ['GROUPA', 'GROUPB', 'GROUPC']

objects = []

for x in range(0, 10):
    objects.append(Obj(random.choice(attrs)))

objects = sorted(objects, key=lambda o: o.attr)

print '['
for o in objects:
    print o
print ']'

Gives me

[
    GROUPA
    GROUPA
    GROUPA
    GROUPA
    GROUPB
    GROUPB
    GROUPB
    GROUPB
    GROUPC
    GROUPC
]

Which is nice, but I would like to define a mapping and take it into account while sorting, for example:

mapping = ['GROUPB', 'GROUPA', 'GROUPC']

result:

[
    GROUPB
    GROUPB
    GROUPB
    GROUPB
    GROUPA
    GROUPA
    GROUPA
    GROUPA
    GROUPC
    GROUPC
]

and then get the result like this

RabbitInAHole
  • 551
  • 1
  • 4
  • 13
  • 2
    Running your example does not output `[ AAABBBCCC ]` as you write, but `[ GROUPA GROUPA GROUPA GROUPA GROUPA GROUPB GROUPB GROUPB GROUPC GROUPC ]`. Maybe you should correct the example, or the example output? – logc Jun 05 '14 at 09:34
  • 1
    What have you tried? Why not implement [comparison operators](https://docs.python.org/2/reference/datamodel.html#object.__lt__) on your class? – jonrsharpe Jun 05 '14 at 09:36

2 Answers2

4

You can define the mapping in a dictionary with the keys as the characters to be mapped and their values integers with the lower value characters having smaller integers as shown below:

mapping = {'B': 1, 'A': 2, 'C': 3}

Then you can use this dictionary in your lambda method as:

sorted(objects, key=lambda o: mapping[o.attr])
anirudh
  • 4,116
  • 2
  • 20
  • 35
-1

You can make use of the cmp argument of the sorted builtin. Define a comparing function where you check whether the elements' corresponding position in the mapping list. Something like:

def compare(a, b):
    return cmp(mapping.index(a), mapping.index(b))

Then, you use it like:

objects = sorted(objects, cmp=compare)

Even better, as some suggested in the comments, you can bypass the cmp argument and directly use key:

objects = sorted(objects, key=lambda o: mapping.index(o.attr))

This way, your code will be compatible with Python 3 and it is much easier to understand.

linkyndy
  • 17,038
  • 20
  • 114
  • 194
  • 3
    `key` is both easier to use and more efficient (it gets called n times, while `cmp` gets called O(n lg n) times): `sorted(objects, key=lambda o: mapping.index(o.attr))`. – Fred Foo Jun 05 '14 at 09:37
  • 2
    `cmp` is deprecated, not available in Python 3, and the OP is already using the newer `key` argument, a much better option. – Martijn Pieters Jun 05 '14 at 09:39
  • Right, I overcomplicated myself :) – linkyndy Jun 05 '14 at 09:39