2013-07-10 1 views
1

У меня есть список объектов. Каждый объект имеет два атрибута: DispName и MachID. DispName может начинаться с theoretical или быть чем-то другим.Сортировка сгруппированных объектов

мне нужно отсортировать этот список следующим образом:

  • первым по алфавиту в MachID.
    • внутри каждой подгруппы MachID первый объект, где имя начинается с theoretical
    • затем другие объекты, отсортированные в алфавитном порядке.

Это код, который я есть сейчас, который работает и производит необходимый выход, но мне было интересно, если я мог бы написать это более вещий, возможно, делает использование groupby? (Мое оправдание для 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 

Производит:

theoretical (MT1) 
map 1 (MT1) 
map 2 (MT1) 
theoretical (MT2) 
map 2 (MT2) 
map 3 (MT2) 

ответ

2
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) 

дает

theoretical (MT1) 
map 1 (MT1) 
map 2 (MT1) 
theoretical (MT2) 
map 2 (MT2) 
map 3 (MT2) 

Существует отличный учебник по How to sort using key functions, здесь.

3

Просто используйте sorted с key, который производит заказ вы хотите. Поскольку кортежи упорядочены лексикографически, key, который создает кортежи, должен работать очень хорошо.

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 
1

Вы можете создать пользовательский компаратор, чтобы описать, как должны быть упорядочены два сопоставления друг относительно друга. Это несколько чище, чем ваш complexSort, поскольку единственная ответственность функции заключается в сравнении двух объектов и оставляет фактическую сортировку на 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 

Результат:

theoretical (MT1) 
map 1 (MT1) 
map 2 (MT1) 
theoretical (MT2) 
map 2 (MT2) 
map 3 (MT2) 
Смежные вопросы