【问题标题】:Unique items in a list with condition有条件的列表中的唯一项目
【发布时间】:2013-07-26 10:59:55
【问题描述】:

如果我在 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。最后,第三个元素也必须对第一个元素是唯一的。

在这个previous question 中,我们解决了一个复杂的方法,在该方法中,交易详细说明了购买的单位。我想在该课程中输出其他单位。如果存在与一门课程中的两个单元相关的两个事务,它将显示它们重复(或每个后续单元的倍数)。

这个问题的目的是确保停止这种重复。由于该解决方案的复杂性,它导致了一系列问题。感谢迄今为止提供帮助的所有人。

【问题讨论】:

  • 不,这是不同的,因为您可以看到我们正在查看多维列表中的各个值。
  • 使用集合来删除重复项,但是集合有一个缺点,因为它没有排序但是有一个令人讨厌的函数 yourset.oderedDict() 或其他东西来订购它左右(不确定,但请查看 python 官方文档套)
  • 我可以不用排序,因为我将在显示的第一个元素上使用 for 循环和 id 语句。我的问题是重复。不是我的强项,所以如果有人为我做了一个很酷的循环,那就太棒了,值得点。谢谢
  • 结果列表不应该是thing = [[20,1,1],[30,1,1]]吗?为什么要保存[20,0,2] 元素?那么第三元素呢?它应该从哪里来?

标签: python


【解决方案1】:

我不确定你是否喜欢这个,但它适用于你的示例:

[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]]
  • @RomanPekar 我认为它必须返回 [[20, 1, 2], [20, 0, 2]] ......但我可能会误解一些东西......
  • [list(i) + j for i, j in {tuple(x[:2]): x[2:] for x in sorted(thing, key=len)}.items()] 将是原始列表理解的更简洁版本。
  • 我不知道,可能是我的英语,我在问题描述中看到“优先考虑第二个元素中的数字 1”。您的解决方案不会检查这种情况。
【解决方案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]]

【讨论】:

  • 这忽略了为带有i[1]==1的项目设置优先级的要求。
  • @simon,我真的很喜欢这个解决方案,除了“1”的优先级不存在。
  • 已针对此要求进行了更新。
  • @Simon 感谢您的帮助,您的解决方案很啰嗦,但确实有效。查看顶部的两个它们很短,我认为资源密集度较低。
【解决方案3】:
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 这个解决方案非常优雅!非常感谢您的帮助。
  • 抱歉 Roman,存在两个可行的解决方案,而 fransua 是第一个给出的,所以我必须根据这个优点给出公认的解决方案。
  • 没有问题,Alex,但我认为 fransua 的解决方案不适用于给定问题的任意输入。另外我认为长列表/字典的理解比简单的循环更不可读和可维护
  • 我现在只是在实施解决方案。会让你知道我的进展情况。
  • 两种解决方案都有效,但您的解决方案保持秩序。谢谢罗马,干得好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-11-26
  • 2019-10-09
  • 1970-01-01
  • 2012-07-27
  • 1970-01-01
  • 1970-01-01
  • 2017-07-06
相关资源
最近更新 更多