【问题标题】:Sort list of lists of tuples of class instances by min attribute value按最小属性值对类实例的元组列表进行排序
【发布时间】:2021-03-29 03:11:14
【问题描述】:

我查看了许多相关问题,但我没有找到专门处理以下问题的问题。我有一个列表列表,其中每个列表都包含元组。这些元组由两个具有id 属性的类实例组成。我只想对 outer 列表进行排序,以便对子列表进行排序,使子列表根据该子列表中所有元组的第一项的属性的最小值进行排序,并且其次是所有元组的第二项的属性的最小值。

这听起来比实际复杂,所以这里有一个例子:

from dataclasses import dataclass

@dataclass
class Char:
    id: int


l = [
    [(Char(1), Char(2)), (Char(2), Char(3))],
    [(Char(4), Char(2)), (Char(5), Char(3))],
    [(Char(0), Char(0))],
    [(Char(-1), Char(1))],
    [(Char(3), Char(-1))],
]

重申:每个子列表都包含包含两个Chars 的元组,它们都有一个id。这些总是顺序的:你可以看到第一个元组的第一项是 1,第二个元组的第一项是 2。这些子列表应该按第一项的最小值排序,然后是第二个的最小值元组项。

那么,预期的输出如下:

l = [
    [(Char(-1), Char(1))],
    [(Char(0), Char(0))],
    [(Char(1), Char(2)), (Char(2), Char(3))],
    [(Char(3), Char(-1))],
    [(Char(4), Char(2)), (Char(5), Char(3))],
]

-1 是元组中所有第一项中的最小值,因此列表必须排在第一位。

以下方法有效,但我不确定是否有更好、更干燥/有效的方法来做到这一点:

l.sort(key=lambda sub: (min([w[0].id for w in sub]),
                        min([w[1].id for w in sub])))

【问题讨论】:

  • 可能是l.sort(key=lambda sub: (sub[0][0].id, sub[0][1].id))?
  • 术语注释:这些不是的元组。它们是Char instances 的元组。这很重要,因为 Python 中的类是一等对象,您可以拥有一个类元组,例如(int, str)

标签: python list sorting


【解决方案1】:

您可以使用order=True 使Char 类实例具有可比性:

@dataclass(order=True)
class Char:
    id: int

因为他们将获得__lt__ 方法,他们将直接在minsort 中使用。那么你可以这样做:

l = [
    [(Char(-1), Char(1))],
    [(Char(0), Char(0))],
    [(Char(1), Char(2)), (Char(2), Char(3))],
    [(Char(3), Char(-1))],
    [(Char(4), Char(2)), (Char(5), Char(3))],
]
l.sort(key=lambda sub:[min(i) for i in zip(*sub)])
print(l)

输出:

[[(Char(id=-1), Char(id=1))], [(Char(id=0), Char(id=0))], [(Char(id=1), Char(id=2)), (Char(id=2), Char(id=3))], [(Char(id=3), Char(id=-1))], [(Char(id=4), Char(id=2)), (Char(id=5), Char(id=3))]]

zip-unpacking 构造转置您的子元素,因此可以在相应元素上使用min,因此每个子元素的lambda 将返回list,其中最小元素来自那些占据元组首位的元素,最小元素来自那些占据第二名等等。请记住,如果元组具有超过 2 个元素,并且假定每个子列表中的所有元组具有完全相同数量的元素,则此解决方案的工作方式将与您的不同。

【讨论】:

  • 感谢您的回复。我不能使用有序的数据类(由于类中的其他事情),但这只是要解决的小问题。所以除了 zip 允许比我的固定到两个示例更大的元组之外,我想我们的解决方案实际上是相同的。我在想operator 有一些内置的东西可以比循环和挖掘更有效地做事。但似乎不是。
  • @BramVanroy 如果要求不要求您使用dataclass,您可以考虑将Char 替换为普通的class,在ids 上比较__lt__ 魔术方法
【解决方案2】:

我想出了以下内容:

In [5]: l.sort(key=lambda sub: sum(ch.id for t in sub for ch in t))

In [6]: l
Out[6]: 
[[(Char(id=0), Char(id=0))],
 [(Char(id=-1), Char(id=1))],
 [(Char(id=3), Char(id=-1))],
 [(Char(id=1), Char(id=2)), (Char(id=2), Char(id=3))],
 [(Char(id=4), Char(id=2)), (Char(id=5), Char(id=3))]]

In [7]: 

【讨论】:

  • 这是不正确的。看看我的“预期输出”。也没有什么需要总结的......
  • 是的,对不起,误解了这个问题
猜你喜欢
  • 2012-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-26
  • 2015-02-24
  • 1970-01-01
  • 2021-07-10
相关资源
最近更新 更多