Je vous propose un nouveau quizz sur le langage Python. La question du jour: comment savoir si un dictionnaire contient des doublons dans ses valeurs?
Trouvez une implémentation rapide pour une fonction has_clones qui retournerait les résultats suivants:
has_clones({1: 1, 2: 1, 3: 3}) #la valeur 1 est en doublon pour les cles 1 et 2
>>> True
has_clones({1: 1, 2: 2, 3: 3}) #Ici pas de doulon
>>> False
Je vous laisse réfléchir un peu, avant de vous proposer d'ici quelques jours une solution possible.
Mise à jour du 12 avril 2011:
Merci à Lothiraldan pour la proposition qui contient juste une petite erreur: list(set(d.values())) et d.values() ne retourneront pas forcément les élements dans le même ordre (un set est une table de hachage et peut donc modifier l'ordre des éléments de la séquence). Il suffirait simplement de faire le test sur le nombre d'éléments des 2 listes.
Ma solution est basée un principe similaire. J'effectue une inversion de dictionnaires, les valeurs devenant les clés et les clés les valeurs. Si une valeur est plusieurs fois présente, elle ne correspondra qu'à une seule clé dans le dictionnaire renversé. Le nombre d'élements du dictionnaire renversé est donc dans ce cas différent de celui du dictionnaire d'origine. En comparant le nombre d'éléments de chacun on peut donc savoir si le dictionnaire d'origine contient des valeurs en double.
C'est assez facile en Python de "retourner" un dictionnaire pour changer les clés en valeurs et vice-versa. La liste de compréhension [(valeur, cle) for (cle, valeur) in d.items()] pemet d'avoir un tuple de couples qui peut être transformé en dictionnaire grâce à la fonction dict.
Voici donc ma proposition de solution pour ce quizz.
d1 = {1: 'un', 2: 'deux', 3:'trois', 4:'quatre', '1': 'un'}
d2 = {1: 'un', 2: 'deux', 3:'trois', 4:'quatre'}
def has_clones(d):
return len(dict([(valeur, cle) for (cle, valeur) in d.items()])) != len(d)
print has_clones(d1)
>>> True
print has_clones(d2)
>>> False



def has_clones(d):
return list(set(d.values())) != d.values()
def has_clones(d):
return len(set(d.values())) != len(d.values())
En terme d'efficacité, j'ai l'impression que ma solution est plus rapide (logique puisqu'elle ne traite que les valeurs).
Dans un shell ipython :
%timeit has_clones(d1) # Solution de l'article : 100000 loops, best of 3: 4.23 us per loop
%timeit has_clones2(d1) # Ma solution : 1000000 loops, best of 3: 1.48 us per loop
Il est souvent intéressant de tester les petits bouts de code python pour voir ce qui prend le plus de temps.
Bonne soirée tout le monde.