如何以偶数频率顺序打印字典键



我想打印出字典键,偶数频率的值对,如

a = 字典("A": 3, "B": 5} => ["A">

, "B", "A", "B", "A", "B", "B", "B"]

a = dict('A': 4, 'B': 1} => ["A", "B", "A", "A", "A"]

我知道我可以使用 while 循环来打印每个键并每次删除计数,直到所有键中的所有值均为 0,但如果有更好的方法呢?

    def func(d: dict):
        res = []
        while any(i > 0 for i in d.values()):
            for k, c in d.items():
                if c > 0:
                    res.append(k)
                    d[k] -= 1
        return res

(我假设你使用的是保证字典迭代顺序的 Python 版本(

这是一个迭代工具式的方法。它为每个字母创建一个生成器,生成给定的字母次数,并将所有这些字母与zip_longest组合在一起,以便它们均匀地生成。

from itertools import repeat, zip_longest
def iterate_evenly(d):
    generators = [repeat(k, v) for k,v in d.items()]
    exhausted = object()
    for round in zip_longest(*generators, fillvalue=exhausted):
        for x in round:
            if x is not exhausted:
                yield x

print(list(iterate_evenly({"A": 3, "B": 5})))
print(list(iterate_evenly({"A": 4, "B": 1})))

结果:

['A', 'B', 'A', 'B', 'A', 'B', 'B', 'B']
['A', 'B', 'A', 'A', 'A']

您可以在更少的行中执行相同的操作,尽管它变得更难阅读。

from itertools import repeat, zip_longest
def iterate_evenly(d):
    exhausted = object()
    return [x for round in zip_longest(*(repeat(k, v) for k,v in d.items()), fillvalue=exhausted) for x in round if x is not exhausted]
print(iterate_evenly({"A": 3, "B": 5}))
print(iterate_evenly({"A": 4, "B": 1}))

对于单行。

首先,创建一个包含两个元素的列表:A s 列表和 B s 列表:

>>> d = {'A': 3, 'B': 5}
>>> [[k]*v for k, v in d.items()]
[['A', 'A', 'A'], ['B', 'B', 'B', 'B', 'B']]

[k]*v的意思是:一个带有v k s的列表。第二,交错AB。我们需要zip_longest,因为zip会在第一个列表结束后停止:

>>> import itertools
>>> list(itertools.zip_longest(*[[k]*v for k, v in d.items()]))
[('A', 'B'), ('A', 'B'), ('A', 'B'), (None, 'B'), (None, 'B')]

现在,只需展平列表并删除None值:

>>> [v for vs in itertools.zip_longest(*[[k]*v for k, v in d.items()]) for v in vs if v is not None]
['A', 'B', 'A', 'B', 'A', 'B', 'B', 'B']

其他例子:

>>> d = {'A': 4, 'B': 1}
>>> [v for vs in itertools.zip_longest(*[[k]*v for k, v in d.items()]) for v in vs if v is not None]
['A', 'B', 'A', 'A', 'A']

您可以将sum与生成器理解一起使用:

res = sum(([key]*value for key, value in d.items()), [])

这利用了这样一个事实,即除了序列乘法("A"*4 == "AAAA"(之外,sum还可以"添加"任何可以使用+运算符的东西,例如列表。

如果您希望顺序随机化,请使用random模块:

from random import shuffle
shuffle(res)

正如 Thierry Lathuille 所指出的,如果你想按原始顺序循环浏览值,你可以使用一些迭代工具魔法:

from itertools import chain, zip_longest
res = [*filter(
    bool,  # drop Nones
    chain(*zip_longest(
        *([key]*val for key, val in d.items()))
    )
)]

作为复制和zip_longest方法的替代方案,让我们尝试简化 OP 的原始代码:

def function(dictionary):
    result = []
    while dictionary:
        result.extend(dictionary)
        dictionary = {k: v - 1 for k, v in dictionary.items() if v > 1}
    return result
print(function({'A': 3, 'B': 5}))
print(function({'A': 4, 'B': 1}))

输出

% python3 test.py
['A', 'B', 'A', 'B', 'A', 'B', 'B', 'B']
['A', 'B', 'A', 'A', 'A']
%

尽管它可能看起来不同,但它对字典参数没有破坏性,这与 OP 的原始代码不同。

也可以使用通过扩展每个字典条目形成的(位置,字符(元组来完成:

a = {'A': 3, 'B': 5}
result = [c for _,c in sorted( (p,c) for c,n in a.items() for p,c in enumerate(c*n))]
print(result) # ['A', 'B', 'A', 'B', 'A', 'B', 'B', 'B']

如果字典的顺序可用,您可以放弃排序并使用它:

result = [c for i in range(max(a.values())) for c,n in a.items() if i<n]

最新更新