Reputation: 22827
I have a list of objects. Each object has two attributes: DispName
and MachID
. DispName
can either start with theoretical
or be something else.
I need to sort this list in the following way:
MachID
.
MachID
subgroup first the object where the name starts with theoretical
This is the code I have now, which works and produces the required output, but I was wondering if I could write this more pythonic, maybe making use of groupby
? (My excuses for the camelCasing).
from collections import defaultdict, namedtuple
from operator import attrgetter
Mapping = namedtuple('Mapping', ['DispName', 'MachID'])
objectList = [Mapping('map 2 (MT1)', 'MT1'),
Mapping('theoretical (MT1)', 'MT1'),
Mapping('map 3 (MT2)', 'MT2'),
Mapping('theoretical (MT2)', 'MT2'),
Mapping('map 1 (MT1)', 'MT1'),
Mapping('map 2 (MT2)', 'MT2')]
def complexSort(objectList):
objectDict = defaultdict(list)
sortedMappingList = []
# group by machine ID
for obj in objectList:
objectDict[obj.MachID].append(obj)
# loop over the mappings sorted alphabetically by machine ID
for machID in sorted(objectDict.keys()):
mappings = objectDict[machID]
nonTheoreticalMappings = []
for mapping in mappings:
if mapping.DispName.startswith('theoretical'):
# if we encounter the theoretical mapping, add it first
sortedMappingList.append(mapping)
else:
# gather the other mappings in a sublist
nonTheoreticalMappings.append(mapping)
# and add that sublist sorted alphabetically
sortedMappingList.extend(sorted(nonTheoreticalMappings,
key=attrgetter('DispName')))
return sortedMappingList
for mapping in complexSort(objectList):
print mapping.DispName
Produces:
theoretical (MT1)
map 1 (MT1)
map 2 (MT1)
theoretical (MT2)
map 2 (MT2)
map 3 (MT2)
Upvotes: 1
Views: 110
Reputation: 76194
You could create a custom comparator to describe how two Mappings should be ordered with respect to one another. This is somewhat cleaner than your complexSort
, since the function's only responsibility is comparing two objects, and leaves the actual sorting to Python.
from collections import namedtuple
Mapping = namedtuple('Mapping', ['DispName', 'MachID'])
def cmp_Mapping(a,b):
#first, sort alphabetically by MachID
if a.MachID != b.MachID:
return cmp(a.MachID, b.MachID)
else:
#if MachIDs match, and one starts with "theoretical", it should go first.
if a.DispName.startswith("theoretical") and not b.DispName.startswith("theoretical"):
return -1
elif b.DispName.startswith("theoretical") and not a.DispName.startswith("theoretical"):
return 1
#everything else is ordered alphabetically.
else:
return cmp(a.DispName, b.DispName)
objectList = [Mapping('map 2 (MT1)', 'MT1'),
Mapping('theoretical (MT1)', 'MT1'),
Mapping('map 3 (MT2)', 'MT2'),
Mapping('theoretical (MT2)', 'MT2'),
Mapping('map 1 (MT1)', 'MT1'),
Mapping('map 2 (MT2)', 'MT2')]
for mapping in sorted(objectList, cmp = cmp_Mapping):
print mapping.DispName
Result:
theoretical (MT1)
map 1 (MT1)
map 2 (MT1)
theoretical (MT2)
map 2 (MT2)
map 3 (MT2)
Upvotes: 1
Reputation: 879451
import collections
import operator
import itertools as IT
Mapping = collections.namedtuple('Mapping', ['DispName', 'MachID'])
objectList = [Mapping('map 2 (MT1)', 'MT1'),
Mapping('theoretical (MT1)', 'MT1'),
Mapping('map 3 (MT2)', 'MT2'),
Mapping('theoretical (MT2)', 'MT2'),
Mapping('map 1 (MT1)', 'MT1'),
Mapping('map 2 (MT2)', 'MT2')]
sortedMappingList = sorted(objectList,
key=lambda mapping:
(mapping.MachID,
not mapping.DispName.startswith('theoretical'),
mapping.DispName))
for key, group in IT.groupby(sortedMappingList, key=operator.attrgetter('MachID')):
for g in group:
print(g.DispName)
yields
theoretical (MT1)
map 1 (MT1)
map 2 (MT1)
theoretical (MT2)
map 2 (MT2)
map 3 (MT2)
There is an excellent tutorial on How to sort using key functions, here.
Upvotes: 2
Reputation: 280500
Just use sorted
with a key
that produces the order you want. Since tuples are ordered lexicographically, a key
that produces tuples should work pretty well.
def sort_key(thing):
return (thing.MachID, not thing.DispName.startswith('theoretical'))
sorted(objectList, key=sort_key) # returns a list sorted the way you want
Upvotes: 3