【问题标题】:Sort list by member variables correctly按成员变量正确排序列表
【发布时间】:2015-11-05 00:26:10
【问题描述】:

我正在尝试按成员变量对对象列表进行排序。通过堆栈溢出,我找到了以下方法。但是, lsort 逐位比较,因此 5、3、7、21、64 将排序为 21、3、5、64、7(我希望这是数字:3、5 , 7, 21, 64)。我不确定如何解决这个问题,因为某些键可能看起来像 D239、D97、D11(lsort 看起来像 D11、D239、D97;我希望它看起来像 D11、D97 , D239)。虽然我更喜欢一种方法,但我想两种就可以了。

import operator 

class foo:
    def __init__(self, key1, data1, data2):
        #all of these values are strings, even though some may be ints
        self.key = key1
        self.d1 = data1
        self.d2 = data2

#sorts list l by member variable search
def lsort (l, search):
    #this doesn't actually work very well.
    #key can be int or string
    #when key is an int, this seems to order by number of digits, then low to high
    #(e.g. 11, 12, 40, 99, 3, 6, 8)
    return sorted(l, key=operator.attrgetter(search))


l1 = [foo('12', 'foo1', None), foo('8', 'qwer', None), foo('7', 'foo3', None), foo('13', 'foo2', None), foo('77', 'foo4', None), foo('12', 'foo5', None) ]


for item in lsort(l1, 'key'):
    print item.key, item.d1, item.d2

输出:

12 foo1 None 
12 foo5 None 
13 foo2 None
7 foo3 None
77 foo4 None
8 qwer None

预期:

7 foo3 None
8 qwer None
12 foo1 None
12 foo5 None
13 foo2 None
77 foo4 None

为什么会这样?我使用相同的排序并在一个非常基本的类上运行它,它似乎工作正常。

class foo:
    def __init__(self, d1):
        self.bar= d1

请帮忙。谢谢。

【问题讨论】:

  • 您到底想要什么顺序?例如,如果值是'22''2x''3',它们的顺序应该是什么?如果您在'22''3' 之间进行数字比较,'3' 位于'22' 之前,但放置'2x' 会很尴尬。
  • 我在上面有一个预期的结果部分。我希望列表按数字键排序,所以 1、5、8、10、15、33、65 等。我将其更改为更清晰。
  • 如果您知道 key 参数将始终是一个 int 或表示 int 的字符串,您可以在设置 init 中的键时进行显式转换,例如 self.key = int(key1) .这将为您提供正确的输出,但如果无法将 key 转换为 int 则会失败
  • @R Nar,并非所有键都是整数,这就是为什么我说有些键可能看起来像 D239、D97 等。

标签: python algorithm sorting data-structures


【解决方案1】:

啊,是的。旧的,“把它放在一个自然的顺序!”问题。

翻译我从 Tye McQueen 在 Perl 中获得的旧技巧,这样的东西应该适用于字符串:

import re

def replace_match(match):
    value = match.group(0)
    if value[0] == ".":
        return value
    else:
        return ("0"*(9-len(value))) + value

def replace_with_natural(string):
    return re.sub("(\.\d*|[1-9]\d{0,8})", replace_match, string)

items = ["hello1", "hello12", "foo12.1", "foo12.05", "hello3", "foo.12.12"]
print(sorted(items, key=replace_with_natural))

我们的想法是我们用一个固定长度的数字替换字符串中的每个数字,这些数字按照我们喜欢的方式按字典顺序排序。

请注意,任何像这样的函数都会遇到处理不佳的问题。在这种情况下,科学记数法处理得不好。但这将在 99.99% 的情况下满足人们对嵌入式数字的期望。

【讨论】:

    【解决方案2】:

    您要确保将键作为整数而不是字符串进行比较,当您使用字符串时,它们按字母顺序排序,即'7' > '11'。最简单的方法是为 foo 类定义自己的自定义比较方法:

    from functools import total_ordering
    
    @total_ordering
    class foo:
        def __init__(self, key1, data1, data2):
            #all of these values are strings, even though some may be ints
            self.key = key1
            self.d1 = data1
            self.d2 = data2
    
        @staticmethod
        def _as_int(value):
            try:
                return int(value)
            except ValueError:
                return value
    
        def __le__(self, other):
            return self._as_int(self.key) < self._as_int(other.key)
        def __eq__(self, other):
            return self._as_int(self.key) == self._as_int(other.key)
    
    l1 = [foo('12', 'foo1', None),
          foo('8', 'qwer', None),
          foo('7', 'foo3', None),
          foo('13', 'foo2', None),
          foo('77', 'foo4', None),
          foo('12', 'foo5', None),
          foo('A', 'foo', None),
          foo('B', 'foo', None)]
    
    for item in sorted(l1):
        print item.key, item.d1, item.d2
    

    这给出了:

    7 foo3 None
    8 qwer None
    12 foo1 None
    12 foo5 None
    13 foo2 None
    77 foo4 None
    A foo None
    B foo None
    

    如果您确定key 属性将是数字,您可以稍微简化一下代码。

    【讨论】:

    • 嗨@Bo Rico。谢谢,它适用于数字。我以前也从未见过 functools!这个功能可以扩展到A12、A8、A7、A13等按键的用例吗?当前行为仍然按数字比较字符串。
    • 关键是让_as_int 将字符串转换为可以按属性排序的对象。如果您使用某种结构化文本,则可以返回一个已排序的元组。即A12XYZ -> ('A', 12, 'XYZ'),'77' -> ('', 77, '')。如何拆分条带、诱导/排除哪些部分以及如何在元组中对它们进行排序将取决于您希望如何对对象进行排序。
    【解决方案3】:

    您正在对字符串进行排序。例如,字符串'12' 位于'2' 之前。如果您想按数字排序,请将它们转换为数字。

    【讨论】:

    • 如前所述,我不只是尝试对字符串进行排序。某些键可能带有字符。
    • @ZtoYi 一个字符是一个字符串,我在解释为​​什么你会得到你得到的行为:你正在对字符串进行排序。请参阅stackoverflow.com/questions/4836710/… 以获得解决方案。
    【解决方案4】:
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-04-21
    • 2017-01-12
    • 1970-01-01
    • 1970-01-01
    • 2012-05-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多