对分组对象进行排序

时间:2013-07-10 17:07:41

标签: python sorting

我有一个对象列表。每个对象都有两个属性:DispNameMachIDDispName可以以theoretical开头,也可以是其他内容。

我需要按以下方式对此列表进行排序:

  • 首先按MachID按字母顺序排列。
    • 在每个MachID子组中,首先是名称以theoretical
    • 开头的对象
    • 然后其他对象按字母顺序排序。

这是我现在的代码,它可以工作并产生所需的输出,但是我想知道我是否可以编写更多的pythonic,也许可以使用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)

3 个答案:

答案 0 :(得分:3)

只需使用sortedkey生成您想要的订单。由于元组按字典顺序排序,产生元组的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 :(得分: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的优秀教程。

答案 2 :(得分: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)