按属性值将列表拆分为子列表

5

我有一个对象数组,每个对象都有一个 suit 属性,我想根据属性值将其分成子数组。目前我正在使用以下代码:

    for c in cards:
        if c.suit.value == 0:
            spades.append(c)
        elif c.suit.value == 1:
            diamonds.append(c)
        elif c.suit.value == 2:
            clubs.append(c)
        else:
            hearts.append(c)

我已经尝试使用itertools.groupby如下:

suits = [list(g) for g in intertools.groupby(cards, lambda x: x.suit.value)]

但这只是产生了:
[[3, <itertools._grouper object at 0x000000000296B2E8>], ...]

我的第一种方法是有效的,我只是想象有一个简单的Python一行代码可以完成我所需的功能。


1
groupby 在“爆发”模式下工作。它会为每个黑桃序列创建一个列表。因此,它可以产生 [[3,[c1,c2]],[1,[c3],[2,[c4,c5,c6]],[3,[c7]]] - Willem Van Onsem
3个回答

9
当对itertools.groupby 进行迭代时,会产生 tuple 对象:键和分组值的可迭代对象(允许数据进行惰性评估)。
你刚刚将tuple转换为了list,这没什么用处。
相反,你需要丢弃键(例如通过解包到_来告诉Python你不使用它)并强制迭代值:
[list(g) for _,g in itertools.groupby(cards, lambda x: x.suit.value)]

如果您想分组并排序,将sorted(cards)传递给groupby并创建单行代码(单行代码很好,但我更喜欢快速的程序)不是很高效。您的方法可以工作,或者您还可以使用collections.defaultdict,甚至可以通过间接方式为字典命名合适的颜色,例如:

import collections

cards = collections.defaultdict(list)
colors = ["spades","diamonds","clubs","hearts"]

for c in cards:
    cards[colors[c.suit.value]].append(c)

这确实会删除元组,但正如Willem Van Onsem上面指出的那样,这会分组成突发事件,而我只想要最多4个子列表,每个花色一个。该列表不一定按花色排序。有没有办法使用groupby来解决这个问题?还是按花色排序是解决这个问题的方法? - user3483203
排序是有效的,但内部运行速度非常缓慢。 - Jean-François Fabre

9

虽然这不是一行代码,但通过使用列表,我们可以使其更加优雅:

spades, diamonds, clubs, hearts = collcard = [[] for _ in range(4)]

for c in cards:
    collcard[c.suit.value].append(c)

因此,我们初始化一个包含四个空子列表的列表,然后将卡片 c 添加到具有索引 c.suit.value 的列表中。

我们使用可迭代解包将第一个元素分配给 spades,第二个元素分配给 diamonds,依此类推。

优点是我们避免了排序(这需要 O(n log n) 的时间复杂度)。因此,该算法的时间复杂度为 O(n)(假设列表附加的平摊成本为 O(1))。

虽然一行代码通常很优雅,但不应该花太多精力编写这些代码,因为一行代码可能更难理解,或者对性能产生重大影响。


1
不是问题的是不要写成一行代码。因为试图创建一行代码会降低性能,就像你最后一次编辑所说的那样。我从Martijn那里得到了这个建议,应该是正确的 :) - Jean-François Fabre
1
也很好,不使用任何字典、集合或其他东西 :) - Jean-François Fabre
我真的很喜欢这个,而且避免排序也很棒!非常感谢! - user3483203
1
你是不是指的是...对于cards中的c: ... ;) - Chris_Rands

1
如果您知道卡牌组的大小,您可以像这样做,考虑到这是一个40张卡牌的组:
allcards = sorted(cards, key=lambda x: x.suit.value)
spades, diamonds, clubs, hearts =  map(lambda x: allcards[x:x+10], range(0,40,10))

基本上你会得到一个排序后的牌堆,然后按照花色大小分块处理。从黑桃到红心,如果它们在较低索引映射到较高索引。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接