【问题标题】:create a dictionary with incrementing values创建一个具有递增值的字典
【发布时间】:2017-08-01 15:28:39
【问题描述】:

我有一个列表,我想生成一个字典 d 取出重复项并排除单个项目,这样第一个键的值为 0,第二个键的值为 1,依此类推。

我写了以下代码:

d = {}
i = 0
for l in a_list:
    if (l not in d) and (l != '<'):
        d[l] = i
        i += 1

如果a_list = ['a', 'b', '&lt;', 'c', 'b', 'd'],运行代码d 后包含{'a': 0, 'b': 1, 'c': 2, 'd':3}。顺序并不重要。 有没有更优雅的方式来获得相同的结果?

【问题讨论】:

  • 您能否根据示例输入提供您期望的示例输出?
  • 如果您可以提供该示例输入/输出并阐明是否需要订购,这将真正有助于明确您正在寻找什么......
  • 需要订购吗?

标签: python dictionary


【解决方案1】:

使用dict.fromkeys 获取您的唯一匹配项(减去您不想要的值),然后使用.update 应用该序列,例如:

a_list = ['a', 'b', '<', 'c', 'b', 'd']

d = dict.fromkeys(el for el in a_list if el != '<')
d.update((k, i) for i, k in enumerate(d))

给你:

{'a': 0, 'b': 1, 'd': 2, 'c': 3}

如果顺序很重要,则使用collections.OrderedDict.fromkeys 保留原始值的顺序,或者如果唯一值应该按字母顺序排列,则对它们进行排序。

【讨论】:

  • 您可以使用if el not in "&lt;" 概括这一点。然后可以适用于任何列入黑名单的字符串,例如"&lt;&gt;{}["
  • 如果有多个后备字符,我宁愿在dict.fromkeys 中使用set(a_list).difference(blacklisted_chars),而不是在理解中使用not in(仅仅是因为它可以具有更好的运行时性能)。
  • d = dict.fromkeys(a_list), d.remove('&lt;') 避免列表理解和多次评估列入黑名单的字符。关注d.update(...)
  • @IceArdor 我明白你的想法了。虽然,没有 list-comp - 只是一个 gen-exp - 所以这没什么大不了的。没有dict.remove 方法,因此您必须使用del d['&lt;'] 并准备不存在的异常或使用d.pop('&lt;', None)。对于单个值,无论是先过滤掉还是保留为减少到字典中的单个键然后删除,都没有太大区别。在多值的情况下,可以很容易地将原来的更改为if el not in "&lt;&gt;!@[]",但是之后根据您的建议需要删除 N 多个键。
【解决方案2】:
{b: a for a, b in enumerate(set(a_list) - {'<'})}

set(a_list)a_list 创建一个集合。 这有效地去除了a_list 中的重复数字,因为set 只能包含唯一值。

【讨论】:

  • 这很优雅。使用集合操作的好主意。
【解决方案3】:

这里需要一个ordereddict并手动过滤列表:

from collections import OrderedDict

d = OrderedDict()

new_list = []

a_list = [1,3,2,3,2,1,3,2,3,1]

for i in a_list:
    if i not in new_list:
        new_list.append(i)

for i, a in enumerate(new_list):
    if a != "<":
       d[i] = a

输出:

OrderedDict([(0, 1), (1, 3), (2, 2)])

如果原始顺序不重要:

final_d = {i:a for i, a in enumerate(set(a_list)) if a != "<"}

【讨论】:

  • 对不起,我忘了提到进一步的要求,我编辑了问题
  • @AreTor 已修复。请参阅我最近的编辑。该代码不会将“d 中。
  • 这个still产生的结果与a_list=[1,3,2,3,2,1,3,2,3,1]的原始代码不同
  • 这比原版更优雅在哪方面?
  • 说明没有相应输入的示例输出比无用更糟糕。
【解决方案4】:

我个人觉得递归非常优雅,尾递归尤其如此:

def f( d, a_list ):
    if a_list:
        if a_list[0] not in d and a_list[0] != '<':
            d[a_list[0]] = len(d)            
        return f( d, a_list[1:] )
    else:
        return d

这样

f( {}, "acbcbabcbabcb" )

将产生

{'a': 0, 'c': 1, 'b': 2}

就像原始代码在相同的输入上所做的一样(按键的模顺序)。

【讨论】:

  • 我已经看到了这个解决方案的许多变体,这是我第一次看到它用递归完成。很整洁!
【解决方案5】:

如果真的:

顺序并不重要。

{k: i for i, k in enumerate(filter(lambda x: x not in "<", set(a_list)))}
# {'a': 3, 'b': 1, 'c': 0, 'd': 2}

编辑:@qnnnez 的回答利用了集合操作,提供了后一个​​代码的优雅版本。

否则,您可以实现 unique_everseen itertools recipe 以保持顺序。为方便起见,您可以从为您实现此配方的库中导入它,即more_itertools

from more_itertools import unique_everseen

{k: i for i, k in enumerate(filter(lambda x: x not in "<", unique_everseen(a_list)))}
# {'a': 0, 'b': 1, 'c': 2, 'd': 3}

【讨论】:

    猜你喜欢
    • 2021-12-04
    • 1970-01-01
    • 2022-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-02
    • 2014-05-24
    • 1970-01-01
    相关资源
    最近更新 更多