列表中满足条件的唯一项

3
如果我在Python中有一个列表,比如说:
thing = [[20,0,1],[20,0,2],[20,1,1],[20,0],[30,1,1]]

我希望你能帮我生成一个列表。
thing = [[20,1,1],[20,0,2],[30,1,1]]

如果第一个元素相同,则删除重复项并优先考虑第二个元素中的数字1。最后,第三个元素必须对第一个元素也是唯一的。
在这个之前的问题中,我们解决了一个复杂的方法,其中对于一个交易,它详细说明了购买的单位。我想输出该课程中的其他单位。如果存在两个与一门课程中的两个单位相关联的交易,则会显示它们为重复项(或每个后续单位的倍数)。
这个问题的目的是确保停止这种重复。由于这个解决方案的复杂性,它已经导致了一系列的问题。感谢到目前为止帮助过我的每个人。

我对没有排序没问题,因为我将在显示的第一个元素上使用一个for循环和一个id语句。我的问题是重复。这不是我的强项,所以如果有人能为我创建一个很酷的循环,那将是很棒并且值得赞赏的。谢谢。 - Alex Stewart
@AlexStewart:所以你不仅想使项目唯一,还想重新排序它们?我想知道为什么在你的例子中,你会期望[[20,1,1],[20,0,2],[30,1,1]]而不是[[20,1,1],[20,0,1],[30,1,1]] - Frerich Raabe
@FrerichRaabe 感谢您的关注,无需下单。 - Alex Stewart
1
结果列表不应该是 thing = [[20,1,1],[30,1,1]] 吗?为什么要保存 [20,0,2] 元素?第三个元素呢?它应该从哪里来? - twil
显示剩余8条评论
3个回答

2

我不确定您是否会喜欢这个,但它可以与您的示例一起使用:

[list(i) + j for i, j in dict([(tuple(x[:2]), x[2:]) for x in sorted(thing, key=lambda x:len(x))]).items()]

编辑:

这里再详细说明一下(请注意,它更符合您对问题的描述,仅按每个子列表的长度排序可能不是最佳解决方案):

thing = [[20,0,1],[20,0,2],[20,1,1],[20,0],[30,1,1]]
dico = {}
for x in thing:
    if not tuple(x[:2]) in dico:
        dico[tuple(x[:2])] = x[2:]
        continue
    if tuple(x[:2])[1] < x[1]:
        dico[tuple(x[:2])] = x[2:]

new_thing = []
for i, j in dico.items():
    new_thing.append(list(i) + j)

它对输入thing = [[20,0,1],[20,0,2],[20,1,1],[20,1,2]]无效,输出应该是[[20, 1, 2], [20, 1, 1]],而你的解决方案给出了[[20, 1, 2], [20, 0, 2]] - Roman Pekar
@RomanPekar 我认为它应该返回[[20, 1, 2], [20, 0, 2]]...但我可能误解了什么... - fransua
1
[list(i) + j for i, j in {tuple(x[:2]): x[2:] for x in sorted(thing, key=len)}.items()] 的写法更加简洁明了。 - sjakobi
我不知道,也许是我的英语有问题,但我在问题描述中看到了“优先考虑第二个元素中的数字1”的要求。你的解决方案没有检查这个条件。 - Roman Pekar

2
你可以尝试使用itertools recipes中的unique_everseen函数。以下是一个排除[20, 0]的解决方案:
from itertools import filterfalse

def unique_everseen(iterable, key=None):
    "List unique elements, preserving order. Remember all elements ever seen."
    # unique_everseen('AAAABBBCCDAABBB') --> A B C D
    # unique_everseen('ABBCcAD', str.lower) --> A B C D
    seen = set()
    seen_add = seen.add
    if key is None:
        for element in filterfalse(seen.__contains__, iterable):
            seen_add(element)
            yield element
    else:
        for element in iterable:
            k = key(element)
            if k not in seen:
                seen_add(k)
                yield element

thing = [[20,0,1],[20,0,2],[20,1,1],[30,1,1]]

thing.sort(key=lambda x: 0 if x[1] == 1 else 1)

print(list(unique_everseen(thing, key=lambda x: (x[0], x[2]))))

输出:

[[20, 1, 1], [30, 1, 1], [20, 0, 2]]

1
忽略了对于i [1] == 1的项目优先级要求。 - RoadieRich
@Simon,我非常喜欢这个解决方案,只是优先级'1'缺失。 - Alex Stewart
1
根据这个要求进行更新。 - sjakobi
1
@Simon,感谢您的帮助,您的解决方案有些冗长,但确实有效。请查看顶部的两个解决方案,它们更简短,我认为资源消耗也更少。 - Alex Stewart

2
thing = [[20,0,1],[20,0,2],[20,1,1],[20,0,1],[30,1,1]]

d = {}
for e in thing:
    k = (e[0], e[2])
    if k not in d or (d[k][1] != 1 and e[1] == 1):
        d[k] = list(e)

print d.values()

[[20, 0, 2], [30, 1, 1], [20, 1, 1]]

如果不需要初始列表:

thing = [[20,0,1],[20,0,2],[20,1,1],[20,0,1],[30,1,1]]

d = {}
for e in thing:
    k = (e[0], e[2])
    if k not in d or (d[k][1] != 1 and e[1] == 1):
        d[k] = e

thing = d.values()

[[20, 0, 2], [30, 1, 1], [20, 1, 1]]

如果您想保持列表的顺序,请使用OrderedDict。

from collections import OrderedDict
d = OrderedDict()

Roman Pekar这个解决方案非常优雅!非常感谢你的帮助。 - Alex Stewart
抱歉,Roman,有两个可行的解决方案,而fransua的是首先给出的,所以我必须根据这个优点给出被接受的解决方案。 - Alex Stewart
1
没问题,Alex,但我认为 Fransua 的解决方案并不适用于给定问题的任意输入。此外,我认为长列表/字典推导式比简单循环的可读性和可维护性要差。 - Roman Pekar
我现在正在实现解决方案。完成后会告诉你进展情况。 - Alex Stewart
两种解决方案都可以,但你的保持了顺序。感谢Roman,做得很棒。 - Alex Stewart
不好意思,我的解决方案没有保持顺序。等一下,我会添加一个选项来保持顺序。 - Roman Pekar

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