【问题标题】:Returning a list of list elements返回列表元素的列表
【发布时间】:2014-10-21 20:39:13
【问题描述】:

我需要帮助编写一个函数,该函数将采用单个列表并返回一个不同的列表,其中列表中的每个元素都在其自己的原始列表中。

我知道我必须遍历我通过的原始列表,然后根据值是否已经在我的列表中附加值,或者创建一个子列表并将该子列表添加到最终列表中。

一个例子是:

input:[1, 2, 2, 2, 3, 1, 1, 3]

Output:[[1,1,1], [2,2,2], [3,3]]

【问题讨论】:

  • 原始列表中的值是否总是整数,或者至少是可散列的?
  • 它们总是整数。不可散列
  • 哈希性是值的特征,而不是类型。整数是可散列的。
  • 啊好吧,我想明白你的意思了。

标签: python list python-3.x append


【解决方案1】:

我会分两步完成:

>>> import collections
>>> inputs = [1, 2, 2, 2, 3, 1, 1, 3]
>>> counts = collections.Counter(inputs)
>>> counts
Counter({1: 3, 2: 3, 3: 2})
>>> outputs = [[key] * count for key, count in counts.items()]
>>> outputs
[[1, 1, 1], [2, 2, 2], [3, 3]]

(事实上,这些恰好是按数字顺序排序的,而且也是按首次出现的顺序,这只是一个巧合。计数器,像普通字典一样,以任意顺序存储它们的键,你应该假设 @ 987654327@ 可能是一个结果。如果这不可接受,您需要多做一些工作。)


那么,它是如何工作的?

第一步创建一个Counter,它只是dict 的一个特殊子类,用于计算每个键的出现次数。关于它的许多漂亮的事情之一是您可以将任何可迭代的(如list)传递给它,它会计算每个元素出现的次数。这是一个微不足道的单行代码,一旦您知道 Counter 的工作原理,它就很明显且可读,而且它甚至可以达到任何可能的效率。*

但这不是您想要的输出格式。我们如何得到它?好吧,我们必须从1: 3(意思是“1”的3 个副本)返回到[1, 1, 1])。你可以写成[key] * count。**剩下的只是一个沼泽标准的列表理解。


如果您查看 collections 模块的文档,它们会以指向 the source 的链接开头。 stdlib 中的许多模块都是这样的,因为它们旨在用作学习的源代码以及可用的代码。因此,您应该能够弄清楚Counter 构造函数是如何工作的。 (它基本上只是调用 _count_elements 函数。)因为这是 Counter 的唯一部分,您实际上在基本 dict 之外使用,您可以自己编写该部分。 (但实际上,一旦您了解了它的工作原理,就没有理由不使用它,对吧?)


* 对于每个元素,它只是进行哈希表查找(并在需要时插入)和+= 1。而在 CPython 中,这一切都发生在 reasonably-optimized C

** 请注意,我们不必担心是否在此处使用 [key] * count[key for _ in range(count)],因为值必须是不可变的,或者至少是“相等性与identity" 类型,否则它们将不能用作键。

【讨论】:

  • 虽然这适用于我的代码,但遗憾的是我还没有了解集合,使用我不熟悉的代码可能不明智。
  • @JohnnyBoy:如果您查看collections 文档,它们以指向the source 的链接开头。 stdlib 中的许多模块都是这样的,因为它们旨在用作学习的源代码以及可用的代码。你能理解Counter 构造函数是如何工作的吗?如果是这样,您可以自己构建它(或者只是觉得使用它很舒服)。
  • @MartijnPieters:不,不是。除了作为迭代器而不是列表之外,它也是扁平的——它只是给你返回1, 1, 1, 2, 2, 2, 3, 3,这和你一开始的时候是一样的。
  • @abarnert 我同意你的观点,一旦我知道它是如何工作的,我应该能够使用它,但是我的教授可能不同意这一点。
  • @JohnnyBoy:尝试自己编写与 _count_elements 等效的代码,使用普通的 dict。然后添加一条评论,说明使用collections.Counter 将如何使其更短且更具可读性,这实际上起到了同样的作用。如果我是你的教授,我会给你一个 A。但只是不加解释地使用它,是的,我可以看到他为此给你打分……
【解决方案2】:

最省时的方法是使用字典:

collector = {}
for elem in inputlist:
    collector.setdefault(elem, []).append(elem)

output = collector.values()

另一个成本更高的选择是排序,然后使用itertools.groupby() 分组:

from itertools import groupby

output = [list(g) for k, g in groupby(sorted(inputlist))]

演示:

>>> inputlist = [1, 2, 2, 2, 3, 1, 1, 3]
>>> collector = {}
>>> for elem in inputlist:
...     collector.setdefault(elem, []).append(elem)
... 
>>> collector.values()
[[1, 1, 1], [2, 2, 2], [3, 3]]
>>> from itertools import groupby
>>> [list(g) for k, g in groupby(sorted(inputlist))]
[[1, 1, 1], [2, 2, 2], [3, 3]]

【讨论】:

  • OP 中没有排序要求。如果预期的行为是让输出按第一次出现的顺序排列,或者顺序无关紧要,那么输出已排序的事实可能是示例中输入的副作用。
  • 在嵌套输出后,您正在做额外的工作来展平输出,这违背了问题的目的(即嵌套数据)。
  • 最后一行提醒我:我希望在 stdlib 中有一个 yield from (list(g) for k, g in groupby(iterable, keyfunc)),或者至少在 more-itertools 中,或者至少是一个我能想到并始终使用的有意义的名称……跨度>
【解决方案3】:

这个怎么样,正如你所说的你想要一个函数:

def makeList(user_list):
    user_list.sort()
    x = user_list[0]
    output = [[]]
    for i in user_list:
        if i == x:
            output[-1].append(i)
        else:
            output.append([i])
        x = i

    return output

>>> print makeList([1, 2, 2, 2, 3, 1, 1, 3])
[[1, 1, 1], [2, 2, 2], [3, 3]]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-05
    • 1970-01-01
    • 2015-05-08
    • 1970-01-01
    • 2021-09-08
    相关资源
    最近更新 更多