【问题标题】:How to increment the values in a dictionary without using a loop?如何在不使用循环的情况下增加字典中的值?
【发布时间】:2021-09-17 17:22:38
【问题描述】:

例如我有以下设置:

dic = {"A":0, "B":0, "C":0} 
tokens = ["A", "B", "C", "C", "D", "E", "F"] 

如果令牌中的元素存在于字典键中,则将该值加 1。

如何在不使用循环的情况下执行此操作? 我现在有以下for 循环

for key in dict.keys(): 
    if key in tokens: 
         dict[key] += 1 

【问题讨论】:

  • 你可以使用collections.Counter()
  • 你可以有一个循环,但你当前的循环效率低下。 (您最终会扫描字典中每个键的列表。)最好循环遍历每个标记,检查该键是否存在于字典中,然后递增。
  • 字典理解算作循环吗? dic = {k:tokens.count(k) for k in dic.keys()}
  • 如果字典理解比 for 循环更快,我会接受。 @约翰尼莫普
  • 你没有说你想要更快 - 只是没有循环。在这种情况下,循环可能会更快,因为在我的示例中,count 为每个键调用,这意味着 3 次通过列表。

标签: python dictionary increment


【解决方案1】:

您可以使用递归方法通过对每个标记逐个切片来更新字典。

dic = {"A":0, "B":0, "C":0} 
tokens = ["A", "B", "C", "C", "D", "E", "F"] 

def key_counter(d, tokens):
    if tokens == []:
        return
    if tokens[0] in d:
        d[tokens[0]] += 1

    key_counter(d, tokens[1:])

key_counter(dic, tokens)

print(dic)

输出

{'A': 1, 'B': 1, 'C': 2}

【讨论】:

  • @Johnny Mopp 和装饰器 functools.lru_cache 递归仍然可以“增强”:)
  • 如果目标是让它更快,这太可怕了,因为切片在每次递归时都会复制列表。
  • @Barmar 老实说,我想以同样的方式使用装饰器(在此示例中),我不会将我的解决方案用于此类任务。我添加它作为评论只是为了提及它的存在,如果使用得当,它真的可以促进递归!
  • 我说的是答案中的解决方案,而不是lru_cache。我不确定这会有什么帮助,因为每次都是不同的参数,所以不会有任何缓存命中。
  • 我认为我从我的第一条评论开始存在一系列误解......我并不是说我的解决方案是最快的或其他......从来没有考虑过性能
【解决方案2】:

这是一个使用Counter无循环版本:

from collections import Counter

dic = {"A": 0, "B": 0, "C": 0}
tokens = ["A", "B", "C", "C", "D", "E", "F"]

res = Counter(filter(dic.__contains__, tokens))
print(res)

输出

{'C': 2, 'A': 1, 'B': 1}

更新

如果dic 中有一个键,而tokens 中不存在,您可以这样做:

from collections import Counter

dic = {"A": 0, "B": 0, "C": 0, "Z": 0}
tokens = ["A", "B", "C", "C", "D", "E", "F"]

dic.update(Counter(filter(dic.__contains__, tokens)))
print(dic)

输出

{'A': 1, 'B': 1, 'C': 2, 'Z': 0}

【讨论】:

  • @AlainT。更新了答案
【解决方案3】:

假设 dic 中的初始值始终为零,您可以将字典键映射到令牌的 count 方法并使用 zip 重构字典:

dic = {"A":0, "B":0, "C":0} 
tokens = ["A", "B", "C", "C", "D", "E", "F"]

dic = dict(zip(dic,map(tokens.count,dic)))
print(dic)
# {'A': 1, 'B': 1, 'C': 2}

这样,您就没有导入库和 for 循环(甚至在推导式中也没有)。

但如果您关心性能并且不介意使用库,我建议您使用 Counter 类而不是列表的 .count() 方法:

 dic = dict(zip(dic,map(Counter(tokens).__getitem__,dic)))

如果 dic 中的初始值可以不为零,则需要将它们添加到:

dic = dict(zip(dic,map(lambda k:dic[k]+tokens.count(k),dic)))
# or 
dic = dict(zip(dic,map((Counter(dic)+Counter(tokens)).__getitem__,dic)))

或者,您可以编写一个递归函数(但这会慢得多,并且会受到最大递归深度的限制):

def countTokens(counts,token,*more):
    if token in counts: counts[token] += 1
    if more: countTokens(counts,*more)

dic = {"A": 0, "B": 0, "C": 0}
tokens = ["A", "B", "C", "C", "D", "E", "F"]

countTokens(dic,*tokens)
print(dic)
{'A': 1, 'B': 1, 'C': 2}

【讨论】:

    【解决方案4】:

    您需要在 tokens 上进行迭代才能全部读取它们,而不仅仅是在您已经设置的键上

    dic = {"A": 0, "B": 0, "C": 0}
    tokens = ["A", "B", "C", "C", "C", "D", "E", "F"]
    
    for token in tokens:
        if token in dic:
            dic[token] += 1
    
    print(dic)  # {'A': 1, 'B': 1, 'C': 3}
    

    如果没有 for 循环,您可以使用 collections.Counter,但这不会过滤

    dic = Counter(tokens)
    print(dic)  # {'C': 3, 'A': 1, 'B': 1, 'D': 1, 'E': 1, 'F': 1}
    

    【讨论】:

    • 但是 OP 不想要一个循环。
    【解决方案5】:

    你可以使用理解

    dic = { k:v+tokens.count(k) for k,v in dic.items() }
    

    但是如果字典有大量的项目并且标记是一个小列表,这是低效的

    【讨论】:

    • for 循环的隐含意义是什么?
    • 你是对的。这是一个 for 循环,所以不是隐含的。
    【解决方案6】:

    = {"A":0, "B":0, "C":0} 令牌 = [“A”、“B”、“C”、“C”、“D”、“E”、“F”] 1# 转换列表以设置令牌 2# 将集合转换为列表 3#排序列表 4# 删除字符 D , E , F 5# 将列表转换为元组 6# 转换为元组到字典并应用字典 7# 用户输入表单的简单技巧

    【讨论】:

    • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
    猜你喜欢
    • 1970-01-01
    • 2022-12-11
    • 1970-01-01
    • 1970-01-01
    • 2015-04-24
    • 1970-01-01
    • 2021-11-04
    • 1970-01-01
    • 2015-03-06
    相关资源
    最近更新 更多