arr
est un tableau de chaînes:
["hello", "world", "stack", "overflow", "hello", "again"]
Quel serait un moyen simple et élégant de vérifier s'il y arr
a des doublons, et si oui, d'en renvoyer un (peu importe lequel)?
Exemples:
["A", "B", "C", "B", "A"] # => "A" or "B"
["A", "B", "C"] # => nil
arr == arr.uniq
serait un moyen simple et élégant de vérifier s'il yarr
a des doublons, mais il ne fournit pas ceux qui ont été dupliqués.Réponses:
Je sais que ce n'est pas une réponse très élégante, mais je l'adore. C'est beau un code de ligne. Et fonctionne parfaitement bien, sauf si vous devez traiter un énorme ensemble de données.
Vous recherchez une solution plus rapide? Voici!
C'est linéaire, O (n), mais doit maintenant gérer plusieurs lignes de code, nécessite des cas de test, etc.
Si vous avez besoin d'une solution encore plus rapide, essayez peut-être C à la place.
Et voici l'essentiel de la comparaison de différentes solutions: https://gist.github.com/naveed-ahmad/8f0b926ffccf5fbd206a1cc58ce9743e
la source
a.select {|e| a.count(e) > 1}.uniq
Vous pouvez le faire de plusieurs manières, la première étant la plus rapide:
Et une option O (N ^ 2) (c'est-à-dire moins efficace):
la source
group_by.select
ary.group_by(&:itself)
. :-)Trouvez simplement la première instance où l'index de l'objet (en partant de la gauche) n'est pas égal à l'index de l'objet (en partant de la droite).
S'il n'y a pas de doublons, la valeur de retour sera nulle.
Je crois que c'est la solution la plus rapide publiée dans le fil jusqu'à présent, car elle ne repose pas sur la création d'objets supplémentaires
#index
et#rindex
est implémentée en C.Le runtime big-O est N ^ 2 et donc plus lent que Sergio, mais le temps du mur pourrait être beaucoup plus rapide en raison du fait que les parties "lentes" tournent en C.la source
arr.find_all {|e| arr.rindex(e) != arr.index(e) }.uniq
arr.detect.with_index { |e, idx| idx != arr.rindex(e) }
. L'utilisationwith_index
devrait supprimer la nécessité de la premièreindex
recherche.detect
ne trouve qu'un seul doublon.find_all
les trouvera tous:la source
count
pour chaque élément du tableau. (Un hachage de comptage, par exemple, est beaucoup plus efficace; par exemple, construisezh = {"A"=>2, "B"=>2, "C"=> 1 }
ensuiteh.select { |k,v| v > 1 }.keys #=> ["A", "B"]
.Voici deux autres façons de trouver un doublon.
Utilisez un ensemble
Utilisez
select
à la place defind
pour renvoyer un tableau de tous les doublons.Utilisation
Array#difference
Drop
.first
pour renvoyer un tableau de tous les doublons.Les deux méthodes retournent
nil
s'il n'y a pas de doublons.J'ai proposé que cela
Array#difference
soit ajouté au noyau Ruby. Plus d'informations sont dans ma réponse ici .Référence
Comparons les méthodes suggérées. Tout d'abord, nous avons besoin d'un tableau pour tester:
et une méthode pour exécuter les benchmarks pour différents tableaux de test:
Je n'ai pas inclus la réponse de @ JjP car un seul duplicata doit être retourné, et lorsque sa réponse est modifiée pour cela, c'est la même chose que la réponse précédente de @ Naveed. Je n'ai pas non plus inclus la réponse de @ Marin, qui, bien que publiée avant la réponse de @ Naveed, a renvoyé tous les doublons plutôt qu'un seul (un point mineur mais il est inutile d'évaluer les deux, car ils sont identiques lorsqu'ils ne renvoient qu'un seul double).
J'ai également modifié d'autres réponses qui renvoyaient tous les doublons pour ne renvoyer que la première trouvée, mais cela ne devrait essentiellement avoir aucun effet sur les performances, car ils ont calculé tous les doublons avant d'en sélectionner un.
Les résultats de chaque benchmark sont répertoriés du plus rapide au plus lent:
Supposons d'abord que le tableau contienne 100 éléments:
Considérons maintenant un tableau avec 10000 éléments:
Notez que ce
find_a_dup_using_difference(arr)
serait beaucoup plus efficace s'ilArray#difference
était implémenté en C, ce qui serait le cas s'il était ajouté au noyau Ruby.Conclusion
Beaucoup de réponses sont raisonnables, mais l' utilisation d'un ensemble est clairement le meilleur choix . Il est le plus rapide dans les cas moyennement durs, le joint le plus rapide dans les cas les plus difficiles et seulement dans les cas informatiques triviaux - lorsque votre choix n'a pas d'importance de toute façon - il peut être battu.
Le cas très particulier dans lequel vous pourriez choisir la solution de Chris serait si vous souhaitez utiliser la méthode pour dédupliquer séparément des milliers de petits tableaux et vous attendez à trouver un doublon généralement moins de 10 éléments. Ce sera un peu plus rapide car cela évite la petite surcharge supplémentaire liée à la création de l'ensemble.
la source
Hélas, la plupart des réponses le sont
O(n^2)
.Voici une
O(n)
solution,Quelle est la complexité de cela?
O(n)
et se brise lors du premier matchO(n)
mémoire, mais seulement la quantité minimaleMaintenant, en fonction de la fréquence des doublons dans votre tableau, ces environnements d'exécution pourraient en fait devenir encore meilleurs. Par exemple, si le tableau de taille
O(n)
a été échantillonné à partir d'une population d'k << n
éléments différents, seule la complexité pour le temps d'exécution et l'espace devientO(k)
, cependant il est plus probable que l'affiche d'origine valide l'entrée et veuille s'assurer qu'il n'y a pas de doublons. Dans ce cas, à la fois la complexité de l'exécution et de la mémoire,O(n)
car nous nous attendons à ce que les éléments n'aient pas de répétitions pour la majorité des entrées.la source
Objets Ruby Array ont une bonne méthode,
select
.La première forme est ce qui vous intéresse ici. Il vous permet de sélectionner des objets qui passent un test.
Objets Ruby Array ont une autre méthode,
count
.Dans ce cas, vous êtes intéressé par les doublons (objets qui apparaissent plus d'une fois dans le tableau). Le test approprié est
a.count(obj) > 1
.Si
a = ["A", "B", "C", "B", "A"]
, alorsVous déclarez que vous ne voulez qu'un seul objet. Alors choisissez-en un.
la source
["A", "B", "B", "A"]
.uniq
sur le tableau.count
pour chaque élément du tableau, ce qui est inutile et inutile. Voir mon commentaire sur la réponse de JjP.find_all () retourne un
array
contenant tous les éléments deenum
pour lesquelsblock
n'est pasfalse
.Pour obtenir des
duplicate
élémentsOu des
uniq
éléments en doublela source
Quelque chose comme ça fonctionnera
Autrement dit, placez toutes les valeurs dans un hachage où key est l'élément du tableau et valeur est le nombre d'occurrences. Sélectionnez ensuite tous les éléments qui se produisent plus d'une fois. Facile.
la source
Je sais que ce fil concerne spécifiquement Ruby, mais j'ai atterri ici pour savoir comment le faire dans le contexte de Ruby on Rails avec ActiveRecord et j'ai pensé que je partagerais également ma solution.
Ce qui précède renvoie un tableau de toutes les adresses e-mail qui sont dupliquées dans la table de base de données de cet exemple (qui dans Rails serait "active_record_classes").
la source
Ceci est une
O(n)
procédure.Vous pouvez également utiliser l'une des lignes suivantes. Aussi O (n) mais une seule itération
la source
Voici mon point de vue sur un grand ensemble de données - comme une table dBase héritée pour trouver des pièces en double
la source
la source
each_with_object
est votre ami!la source
Ce code renverra la liste des valeurs dupliquées. Les clés de hachage sont utilisées comme un moyen efficace de vérifier quelles valeurs ont déjà été vues. Selon que la valeur a été vue, le tableau d'origine
ary
est partitionné en 2 tableaux: le premier contenant des valeurs uniques et le second contenant des doublons.Vous pouvez le raccourcir davantage - mais au prix d'une syntaxe légèrement plus complexe - à cette forme:
la source
Résultats
la source
Si vous comparez deux tableaux différents (au lieu d'un par rapport à lui-même), un moyen très rapide consiste à utiliser l'opérateur d'intersection
&
fourni par la classe Ruby's Array .la source
J'avais besoin de savoir combien il y avait de doublons et ce qu'ils étaient, alors j'ai écrit une fonction basée sur ce que Naveed avait publié plus tôt:
la source
démontrons dans l'implémentation du code
Appelez maintenant la méthode de duplication et affichez le résultat de retour -
la source
[1,2,3].uniq!.nil? => true
[1,2,3,3].uniq!.nil? => false
Notez que ce qui précède est destructeur
la source