2009-08-06 8 views
5

Newbie question ici, alors s'il vous plaît ours avec moi.Comment filtrer un dictionnaire par valeur?

Disons que j'ai un dictionnaire qui ressemble à ceci:

a = {"2323232838": ("first/dir", "hello.txt"), 
    "2323221383": ("second/dir", "foo.txt"), 
    "3434221": ("first/dir", "hello.txt"), 
    "32232334": ("first/dir", "hello.txt"), 
    "324234324": ("third/dir", "dog.txt")} 

Je veux toutes les valeurs qui sont égales entre elles pour être déplacé dans un autre dictionnaire.

matched = {"2323232838": ("first/dir", "hello.txt"), 
      "3434221": ("first/dir", "hello.txt"), 
      "32232334": ("first/dir", "hello.txt")} 

Et les éléments inégalés restants devraient être ressembler à ceci:

remainder = {"2323221383": ("second/dir", "foo.txt"), 
      "324234324": ("third/dir", "dog.txt")} 

Merci à l'avance, et si vous donner un exemple, s'il vous plaît commenter autant que possible.

+0

Comme les autres l'ont dit, vous ne pouvez tout simplement pas avoir de dictionnaires comme vous le dites dans vos exemples. Vous avez plusieurs valeurs de unique_id, dir et file, et cela n'est pas légal. Les clés du dictionnaire sont uniques. –

+0

OH! Mon mauvais, désolé à ce sujet, je vais le corriger. –

+0

Votre correction n'est même pas syntaxiquement correcte Python. Peut-être pourriez-vous demander "Comment représenterais-je les données suivantes dans les structures de données Python?" puis décrivez vos données. –

Répondre

1

sur un dictionnaire Itère ne diffère pas de itérer sur une liste en python:

for key in dic: 
    print("dic[%s] = %s" % (key, dic[key])) 

Ce imprimera toutes les clés et les valeurs de votre dictionnaire.

+0

Alors que vous avez raison, cela a été traité dans les commentaires, et ne répond pas à sa question, qui était déductible. – Triptych

1

Je suppose que votre identifiant unique sera la clé.
Probablement pas très belle, mais renvoie un dict avec vos valeurs uniques:

>>> dict_ = {'1': ['first/dir', 'hello.txt'], 
'3': ['first/dir', 'foo.txt'], 
'2': ['second/dir', 'foo.txt'], 
'4': ['second/dir', 'foo.txt']} 
>>> dict((v[0]+v[1],k) for k,v in dict_.iteritems()) 
{'second/dir/foo.txt': '4', 'first/dir/hello.txt': '1', 'first/dir/foo.txt': '3'} 

Je vous ai vu à jour votre message:

>>> a 
{'324234324': ('third/dir', 'dog.txt'), 
'2323221383': ('second/dir', 'foo.txt'), 
'3434221': ('first/dir', 'hello.txt'), 
'2323232838': ('first/dir', 'hello.txt'), 
'32232334': ('first/dir', 'hello.txt')} 
>>> dict((v[0]+"/"+v[1],k) for k,v in a.iteritems()) 
{'second/dir/foo.txt': '2323221383', 
'first/dir/hello.txt': '32232334', 
'third/dir/dog.txt': '324234324'} 
+0

ce n'est pas ce que OP a demandé du tout. – SilentGhost

+0

Comme le vôtre ne l'est pas, aussi. L'OP avait une version différente au début qui m'a dérouté. Version Tryptichs semble être bien, cependant. – buster

10

Le code ci-dessous se traduira par deux variables, et matchesremainders. matches est un tableau de dictionnaires dans lequel les éléments correspondants du dictionnaire d'origine auront un élément correspondant. remainder contiendra, comme dans votre exemple, un dictionnaire contenant tous les éléments sans correspondance.

Notez que dans votre exemple, il existe un seul ensemble de valeurs correspondantes: ('first/dir', 'hello.txt'). S'il y avait plus d'un ensemble, chacun aurait une entrée correspondante dans matches.

import itertools 

# Original dict 
a = {"2323232838": ("first/dir", "hello.txt"), 
    "2323221383": ("second/dir", "foo.txt"), 
    "3434221": ("first/dir", "hello.txt"), 
    "32232334": ("first/dir", "hello.txt"), 
    "324234324": ("third/dir", "dog.txt")} 

# Convert dict to sorted list of items 
a = sorted(a.items(), key=lambda x:x[1]) 

# Group by value of tuple 
groups = itertools.groupby(a, key=lambda x:x[1]) 

# Pull out matching groups of items, and combine items 
# with no matches back into a single dictionary 
remainder = [] 
matched = [] 

for key, group in groups: 
    group = list(group) 
    if len(group) == 1: 
     remainder.append(group[0]) 
    else: 
     matched.append(dict(group)) 
else: 
    remainder = dict(remainder) 

Sortie:

>>> matched 
[ 
    { 
    '3434221': ('first/dir', 'hello.txt'), 
    '2323232838': ('first/dir', 'hello.txt'), 
    '32232334': ('first/dir', 'hello.txt') 
    } 
] 

>>> remainder 
{ 
    '2323221383': ('second/dir', 'foo.txt'), 
    '324234324': ('third/dir', 'dog.txt') 
} 

En tant que débutant, vous êtes probablement introduit à quelques concepts inconnus dans le code ci-dessus. Voici quelques liens:

+0

sympa. Je peux voir maintenant que j'ai mal compris la question avec ma réponse. En tout cas, ça me va bien :) – buster

+0

Merci, je vais devoir lire sur les groupes, mais c'est tout bon, merci un million. Merci aussi d'avoir édité ma question! –

+0

Remarque, len (group) vaut 1 len (group) == 1. Bien que le test d'identité ("is") fonctionne ici dans cPython en raison de la mise en cache de petits entiers, c'est une mauvaise habitude à prendre. Vous voulez un test d'égalité. –

0

si vous connaissez la valeur que vous voulez filtrer:

known_tuple = 'first/dir','hello.txt' 
b = {k:v for k, v in a.items() if v == known_tuple} 

alors a deviendraient:

a = dict(a.items() - b.items()) 

cette notation est Py3K, mais je suis sûr que quelque chose similaire peut être implémenté dans les versions héritées. Si vous ne savez pas ce que le known_tuple est, alors vous devez d'abord le trouver. par exemple comme ceci:

c = list(a.values()) 
for i in set(c): 
    c.remove(i) 
known_tuple = c[0] 
+0

Non, ça peut très bien être "third/dir", "something.txt", je ne sais pas. –

4

Qu'est-ce que vous demandez est appelé un « indice inverti » - les éléments distincts sont enregistrés une fois avec une liste de clés.

>>> from collections import defaultdict 
>>> a = {"2323232838": ("first/dir", "hello.txt"), 
...  "2323221383": ("second/dir", "foo.txt"), 
...  "3434221": ("first/dir", "hello.txt"), 
...  "32232334": ("first/dir", "hello.txt"), 
...  "324234324": ("third/dir", "dog.txt")} 
>>> invert = defaultdict(list) 
>>> for key, value in a.items(): 
...  invert[value].append(key) 
... 
>>> invert 
defaultdict(<type 'list'>, {('first/dir', 'hello.txt'): ['3434221', '2323232838', '32232334'], ('second/dir', 'foo.txt'): ['2323221383'], ('third/dir', 'dog.txt'): ['324234324']}) 

Le dictionnaire inversé a les valeurs d'origine associées à une liste de 1 ou plusieurs clés.

Maintenant, pour obtenir vos dictionnaires révisés à partir de cela.

Filtrage:

>>> [ invert[multi] for multi in invert if len(invert[multi]) > 1 ] 
[['3434221', '2323232838', '32232334']] 
>>> [ invert[uni] for uni in invert if len(invert[uni]) == 1 ] 
[['2323221383'], ['324234324']] 

expansion

>>> [ (i,multi) for multi in invert if len(invert[multi]) > 1 for i in invert[multi] ] 
[('3434221', ('first/dir', 'hello.txt')), ('2323232838', ('first/dir', 'hello.txt')), ('32232334', ('first/dir', 'hello.txt'))] 
>>> dict((i,multi) for multi in invert if len(invert[multi]) > 1 for i in invert[multi]) 
{'3434221': ('first/dir', 'hello.txt'), '2323232838': ('first/dir', 'hello.txt'), '32232334': ('first/dir', 'hello.txt')} 

Un traitement similaire (mais plus simple) fonctionne pour les éléments qui se produisent une fois.

+0

Huh, très simple, dois utiliser le lib standard python. plus, merci pour cela. –

+0

Ah, sympa aussi. C'est incroyable ce que vous pouvez faire avec des appels standards simples :) – buster

Questions connexes