192 votes

Comment trouver et retourner une valeur dupliquée dans un tableau ?

arr est un tableau de chaînes de caractères :

["hello", "world", "stack", "overflow", "hello", "again"]

Quel serait un moyen simple et élégant de vérifier si arr a des doublons, et si c'est le cas, retourner l'un d'entre eux (peu importe lequel) ?

Exemples :

["A", "B", "C", "B", "A"]    # => "A" or "B"
["A", "B", "C"]              # => nil

0 votes

arr == arr.uniq serait une manière simple et élégante de vérifier si arr a des doublons, cependant, il ne fournit pas lesquels ont été doublés.

280voto

Naveed Points 4948
a = ["A", "B", "C", "B", "A"]
a.detect{ |e| a.count(e) > 1 }

Je sais que ce n'est pas une réponse très élégante, mais je l'adore. C'est un beau code en une ligne. Et il fonctionne parfaitement bien, sauf si vous avez besoin de traiter un énorme ensemble de données.

Vous cherchez une solution plus rapide ? C'est ici que ça se passe !

def find_one_using_hash_map(array)
  map = {}
  dup = nil
  array.each do |v|
    map[v] = (map[v] || 0 ) + 1

    if map[v] > 1
      dup = v
      break
    end
  end

  return dup
end

Il est linéaire, O(n), mais doit maintenant gérer de multiples lignes de code, a besoin de cas de test, etc.

Si vous avez besoin d'une solution encore plus rapide, essayez plutôt le C.

Et voici l'essentiel de la comparaison des différentes solutions : https://gist.github.com/naveed-ahmad/8f0b926ffccf5fbd206a1cc58ce9743e

60 votes

Sauf quadratique pour quelque chose qui peut être résolu en temps linéaire.

21 votes

Fournir des solutions O(n^2) pour des problèmes linéaires n'est pas la voie à suivre.

23 votes

@jasonmp85 - C'est vrai ; cependant, cela ne prend en compte que le temps d'exécution big-O. En pratique, à moins que vous n'écriviez ce code pour des données à grande échelle (et si c'est le cas, vous pouvez tout simplement utiliser C ou Python), la réponse fournie est bien plus élégante/lisible, et ne va pas s'exécuter beaucoup plus lentement par rapport à une solution en temps linéaire. en outre, en théorie, la solution en temps linéaire nécessite un espace linéaire, qui n'est pas forcément disponible.

234voto

Ryan LeCompte Points 1784

Vous pouvez le faire de plusieurs façons, la première étant la plus rapide :

ary = ["A", "B", "C", "B", "A"]

ary.group_by{ |e| e }.select { |k, v| v.size > 1 }.map(&:first)

ary.sort.chunk{ |e| e }.select { |e, chunk| chunk.size > 1 }.map(&:first)

Et une option O(N^2) (c'est-à-dire moins efficace) :

ary.select{ |e| ary.count(e) > 1 }.uniq

19 votes

Les deux premières sont beaucoup plus efficaces pour les grands tableaux. La dernière est O(n*n) et peut donc être lente. J'ai eu besoin de l'utiliser pour un tableau de ~20k éléments et les deux premières sont revenues presque instantanément. J'ai dû annuler la troisième parce qu'elle prenait trop de temps. Merci !

5 votes

Juste une observation mais les deux premiers qui se terminent par .map(&:first) pourrait simplement se terminer par .keys car cette partie ne fait que tirer les clés d'un hash.

0 votes

@engineerDave cela dépend de la version de ruby utilisée. La version 1.8.7 nécessiterait &:first ou même {|k,_| k } sans ActiveSupport.

45voto

Chris Heald Points 28814

Il suffit de trouver le premier cas où l'indice de l'objet (en comptant à partir de la gauche) n'est pas égal à l'indice de l'objet (en comptant à partir de la droite).

arr.detect {|e| arr.rindex(e) != arr.index(e) }

S'il n'y a pas de doublons, la valeur de retour sera nulle.

Je pense que c'est la solution la plus rapide affichée dans le fil de discussion jusqu'à présent, car elle ne repose pas sur la création d'objets supplémentaires, et #index y #rindex sont implémentés en C. Le temps d'exécution de big-O est N^2 et donc plus lent que celui de Sergio, mais le temps de paroi pourrait être beaucoup plus rapide en raison du fait que les parties "lentes" sont exécutées en C.

5 votes

J'aime cette solution, mais elle ne renvoie que le premier doublon. Pour trouver tous les doublons : arr.find_all {|e| arr.rindex(e) != arr.index(e) }.uniq

1 votes

Votre réponse ne montre pas non plus comment trouver s'il y a des triples, ou si l'on peut tirer des éléments du tableau pour épeler "CAT".

3 votes

@bruno077 Comment est-ce le temps linéaire ?

34voto

JjP Points 311

detect ne trouve qu'un seul doublon. find_all les trouvera tous :

a = ["A", "B", "C", "B", "A"]
a.find_all { |e| a.count(e) > 1 }

4 votes

La question précise bien qu'un seul exemplaire doit être rendu. Je pense que montrer comment trouver tous les doublons est bien, mais seulement en tant qu'aparté d'une réponse qui répond à la question posée, ce que vous n'avez pas fait. En fait, il est terriblement inefficace de faire appel à count pour chaque élément du tableau. (Un hachage de comptage, par exemple, est beaucoup plus efficace ; par exemple, construire h = {"A"=>2, "B"=>2, "C"=> 1 } puis h.select { |k,v| v > 1 }.keys #=> ["A", "B"] .

16voto

Martin Velez Points 592

Les objets Ruby Array disposent d'une excellente méthode, select .

select {|item| block }  new_ary
select  an_enumerator

C'est la première forme qui vous intéresse ici. Il vous permet de sélectionner les objets qui passent un test.

Les objets Ruby Array ont une autre méthode, count .

count  int
count(obj)  int
count { |item| block }  int

Dans ce cas, vous vous intéressez aux 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"] alors

a.select{|item| a.count(item) > 1}.uniq
=> ["A", "B"]

Vous déclarez que vous voulez seulement un objet. Alors choisissez-en un.

1 votes

J'aime beaucoup celui-là, mais tu dois mettre un uniq à la fin ou tu obtiendras ["A", "B", "B", "A"]

1 votes

Excellente réponse. C'est exactement ce que je recherchais. Comme @Joeyjoejoejr l'a souligné. J'ai soumis une modification pour mettre .uniq sur le tableau.

1 votes

C'est extrêmement inefficace. Non seulement vous trouvez tous les doublons, puis vous les jetez tous sauf un, mais en plus vous invoquez count pour chaque élément du tableau, ce qui est un gaspillage et inutile. Voir mon commentaire sur la réponse de JjP.

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