2009-10-19 10 views
7

Quelle est la méthode la plus rapide/simple pour supprimer les doublons dans un tableau d'objets, en fonction d'une clé spécifique: valeur ou d'un résultat renvoyé par une méthode? Par exemple, j'ai 20 nœuds d'élément XML qui ont tous le même nom, mais ils ont des valeurs de «texte» différentes, dont certaines sont des doublons. Je voudrais supprimer les doublons en disant "if element.text == previous_element.text, le supprimer". Comment est-ce que je fais ça dans Ruby avec la plus petite quantité de code?Le moyen le plus rapide/à une ligne pour supprimer les doublons (par clé) dans Ruby Array?

J'ai vu comment le faire pour des valeurs simples de chaîne/nombre entier, mais pas pour les objets.

+0

Voir ma réponse pour une version moderne. –

Répondre

14

Voici la méthode de hachage standard. Notez l'utilisation de ||= opérateur, qui est un moyen plus pratique (a ||= b) d'écrire a = b unless a.

array.inject({}) do |hash,item| 
    hash[item.text]||=item 
    hash 
end.values.inspect 

Vous pouvez le faire sur une seule ligne non plus.

Le script nécessite des vérifications d'égalité O (n) des chaînes text. C'est ce qui est couvert par O (n) quand vous voyez un hachage.

+0

Pas exactement le plus rapide, car il s'exécute en O (n^2) fois. Là encore, ce n'est pas vraiment important compte tenu du temps CPU. – EmFi

+1

@EmFi, l'accès à la table de hachage ne prend pas O (n) (nous devrions itérer la chaîne 'text', mais nous devrons le faire de toute façon). Je viens de poster une réponse à ce sujet: http://stackoverflow.com/questions/1590405/distinguishing-extra-element-from-two-arrays/1590536#1590536 –

+0

@Pavel Désolé, vous avez raison. Je me suis embrouillé pendant une seconde en pensant que l'appel des valeurs ajoutées en faisait O (n^2). Quand ça fait juste O (2n). – EmFi

10

Ce fait tout:

Hash[*a.map{|x| [x.text, x]}].values 

court? oui.

(l'astérisque est optionnel, semble être requis pour 1.8.6).

Par exemple:

a = [Thing.new('a'), Thing.new('b'), Thing.new('c'), Thing.new('c')] 
=> [#<Thing a>, #<Thing b>, #<Thing c>, #<Thing c>] 

Hash[a.map{|x| [x.text, x]}].values 
=> [#<Thing a>, #<Thing b>, #<Thing c>] 

partie ennuyeuse: voici la petite classe de test je:

class Thing 
    attr_reader :text 
    def initialize(text) 
    @text = text 
    end 

    def inspect 
    "#<Thing #{text}>" 
    end 
end 
+0

c'est vraiment cool, c'est quoi (&: last)? –

+0

il est parti dans la nouvelle version, encore plus courte, plus simple :). Cependant, dire 'ary.map {| x | x.last} 'et' ary.map (&: last) 'sont équivalents. – Peter

+0

J'ai l'erreur suivante: dans '[] ': nombre impair d'arguments pour Hash (ArgumentError) –

4

Utilisez Array#uniq avec un bloc. Dans votre cas:

array.uniq(&:text) # => array with duplicated `text` removed 

Ceci a été introduit dans Ruby 1.9.2, donc si vous utilisez une version antérieure, vous pouvez utiliser backports avec require 'backports/1.9.2/array/uniq'

Questions connexes