117 votes

Python : Liste de dict, si elle existe incrémente une valeur de dict, sinon ajoute un nouveau dict.

J'aimerais faire quelque chose comme ça.

list_of_urls = ['http://www.google.fr/', 'http://www.google.fr/', 
                'http://www.google.cn/', 'http://www.google.com/', 
                'http://www.google.fr/', 'http://www.google.fr/', 
                'http://www.google.fr/', 'http://www.google.com/', 
                'http://www.google.fr/', 'http://www.google.com/', 
                'http://www.google.cn/']

urls = [{'url': 'http://www.google.fr/', 'nbr': 1}]

for url in list_of_urls:
    if url in [f['url'] for f in urls]:
         urls[??]['nbr'] += 1
    else:
         urls.append({'url': url, 'nbr': 1})

Comment puis-je faire ? Je ne sais pas si je dois prendre le tuple pour le modifier ou déterminer les indices du tuple ?

Une aide ?

224voto

steveha Points 24808

C'est une façon très étrange d'organiser les choses. Si vous stockez dans un dictionnaire, c'est facile :

# This example should work in any version of Python.
# urls_d will contain URL keys, with counts as values, like: {'http://www.google.fr/' : 1 }
urls_d = {}
for url in list_of_urls:
    if not url in urls_d:
        urls_d[url] = 1
    else:
        urls_d[url] += 1

Ce code pour la mise à jour d'un dictionnaire de comptes est un "motif" commun en Python. Il est si commun qu'il existe une structure de données spéciale, defaultdict créé juste pour rendre la chose encore plus facile :

from collections import defaultdict  # available in Python 2.5 and newer

urls_d = defaultdict(int)
for url in list_of_urls:
    urls_d[url] += 1

Si vous accédez au defaultdict à l'aide d'une clé, et que la clé n'est pas déjà dans l'application defaultdict la clé est automatiquement ajoutée avec une valeur par défaut. Le site defaultdict prend le callable que vous avez passé, et l'appelle pour obtenir la valeur par défaut. Dans ce cas, nous avons passé la classe int ; lorsque Python appelle int() il renvoie une valeur nulle. Ainsi, la première fois que vous faites référence à une URL, son compte est initialisé à zéro, puis vous ajoutez un au compte.

Mais un dictionnaire rempli de comptes est également un modèle courant, c'est pourquoi Python fournit une classe prête à l'emploi : containers.Counter Il suffit de créer un Counter en appelant la classe, en passant n'importe quel itérable ; elle construit un dictionnaire où les clés sont des valeurs de l'itérable, et les valeurs sont des comptes de combien de fois la clé est apparue dans l'itérable. L'exemple ci-dessus devient alors :

from collections import Counter  # available in Python 2.7 and newer

urls_d = Counter(list_of_urls)

Si vous devez vraiment procéder de la manière indiquée, la méthode la plus simple et la plus rapide consiste à utiliser l'un de ces trois exemples, puis à construire celui dont vous avez besoin.

from collections import defaultdict  # available in Python 2.5 and newer

urls_d = defaultdict(int)
for url in list_of_urls:
    urls_d[url] += 1

urls = [{"url": key, "nbr": value} for key, value in urls_d.items()]

Si vous utilisez Python 2.7 ou une version plus récente, vous pouvez le faire en une seule ligne :

from collections import Counter

urls = [{"url": key, "nbr": value} for key, value in Counter(list_of_urls).items()]

0 votes

J'aime bien l'envoyer à un modèle django pour pouvoir faire : `{% for u in urls %} {{ u.url }} : {{ u.nbr }}{% endfor %}

3 votes

Vous pouvez toujours faire {% for url, nbr in urls.items %}{{ url }} : {{ nbr }}{% endfor %}

188voto

mikelikespie Points 2560

L'utilisation de la valeur par défaut fonctionne, mais c'est aussi le cas :

urls[url] = urls.get(url, 0) + 1

en utilisant .get vous pouvez obtenir une déclaration par défaut si elle n'existe pas. Par défaut, c'est None, mais dans le cas que je vous ai envoyé, ce serait 0.

16 votes

En fait, je pense que c'est la meilleure réponse, puisqu'elle est agnostique par rapport au dictionnaire donné, ce qui est un énorme bonus.

0 votes

C'est une solution propre et agréable.

2 votes

Cela devrait être la réponse. Efficace, propre et direct ! !! J'espère que stackoverflow permettra à la communauté de décider de la réponse avec l'auteur de la question.

28voto

Greg Hewgill Points 356191

Utilisez Par défaut, :

from collections import defaultdict

urls = defaultdict(int)

for url in list_of_urls:
    urls[url] += 1

0 votes

Paraphrase de la solution de mikelikespie

18voto

mossplix Points 1495

Cela fonctionne toujours bien pour moi :

for url in list_of_urls:
    urls.setdefault(url, 0)
    urls[url] += 1

0 votes

Paraphrase de la solution de mikelikespie

3voto

NicDumZ Points 5566

Pour le faire exactement à votre façon ? Vous pourriez utiliser la structure for...else

for url in list_of_urls:
    for url_dict in urls:
        if url_dict['url'] == url:
            url_dict['nbr'] += 1
            break
    else:
        urls.append(dict(url=url, nbr=1))

Mais c'est assez inélégant. Devez-vous vraiment stocker les urls visitées sous forme de LISTE ? Si vous le triez comme un dict, indexé par chaîne d'url, par exemple, ce serait beaucoup plus propre :

urls = {'http://www.google.fr/': dict(url='http://www.google.fr/', nbr=1)}

for url in list_of_urls:
    if url in urls:
        urls[url]['nbr'] += 1
    else:
        urls[url] = dict(url=url, nbr=1)

Quelques éléments à noter dans ce deuxième exemple :

  • voir comment l'utilisation d'un dict pour urls supprime la nécessité de passer par tout le urls lors du test d'une seule url . Cette approche sera plus rapide.
  • Utilisation de dict( ) au lieu d'accolades rend votre code plus court
  • en utilisant list_of_urls , urls y url comme noms de variables rendent le code assez difficile à analyser. Il est préférable de trouver quelque chose de plus clair, tel que urls_to_visit , urls_already_visited y current_url . Je sais, c'est plus long. Mais c'est plus clair.

Et bien sûr, je suppose que dict(url='http://www.google.fr', nbr=1) est une simplification de votre propre structure de données, car sinon, urls pourrait simplement être :

urls = {'http://www.google.fr':1}

for url in list_of_urls:
    if url in urls:
        urls[url] += 1
    else:
        urls[url] = 1

Ce qui peut devenir très élégant avec le Par défaut, l'attitude :

urls = collections.defaultdict(int)
for url in list_of_urls:
    urls[url] += 1

0 votes

La deuxième version est bonne puisque je peux convertir le dict comme une liste après.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X