Supprimer les éléments en double du tableau dans Ruby


325

J'ai un tableau Ruby qui contient des éléments en double.

array = [1,2,2,1,4,4,5,6,7,8,5,6]

Comment puis-je supprimer tous les éléments en double de ce tableau tout en conservant tous les éléments uniques sans utiliser de boucles for et d'itération?

Réponses:


722
array = array.uniq

uniq supprime tous les éléments en double et conserve tous les éléments uniques du tableau.

C'est l'une des nombreuses beautés de la langue Ruby.


50
non, l'uniq! retournera nil si le tableau était encore unique Ex: a = [1,2,3,4] a.uniq -> [1,2,3,4] mais a.uniq! -> nil
duykhoa

15
je ne verrais pas vraiment cela comme une beauté de la langue ruby ​​... c'est juste la beauté de la bibliothèque standard ruby? ne vous méprenez pas, il y a beaucoup de belles choses sur la langue.
Justin L.

7
écrivez la même chose en Objective-C, Javascript et PHP. Alors dites-nous que Ruby n'est pas une belle langue!
Adam Waite

3
Cela fonctionne également pour les types complexes: [{how: "are"}, {u:"doing"}, {how: "are"}].uniq => [{:how=>"are"}, {:u=>"doing"}]
Blaskovicz

5
à propos de ce que dit @duykhoa, l'uniq! la méthode retourne nil, mais vous ne vous souciez généralement pas du retour d'un .uniq!il fait le travail sur l'objet lui
carpinchosaurio

82

Vous pouvez renvoyer l'intersection.

a = [1,1,2,3]
a & a

Cela supprimera également les doublons.


12
Sur le plan fonctionnel, cette réponse est correcte, mais je pense que cela est nettement moins lisible que d'utiliser simplement uniq.
Fiona T

21
Je le mettais juste ici, donc, quiconque visite cette page verra également d'autres façons de le faire, je n'essayais pas de dire que c'est mieux en aucune façon.
jaredsmith

3
La raison pour laquelle cela fonctionne est que lorsque vous utilisez des opérations d'ensemble, le tableau résultant est traité comme un ensemble, qui est une structure de données qui n'a généralement pas de valeurs de répétition. Utiliser a | a(union) ferait le même tour.
Cezar

47

Vous pouvez supprimer les éléments en double avec la méthode uniq:

array.uniq  # => [1, 2, 4, 5, 6, 7, 8]

Ce qui pourrait également être utile de savoir, c'est que uniqprend un bloc, donc si vous avez un tableau de clés:

["bucket1:file1", "bucket2:file1", "bucket3:file2", "bucket4:file2"]

et vous voulez savoir quels sont les fichiers uniques, vous pouvez le découvrir avec:

a.uniq { |f| f[/\d+$/] }.map { |p| p.split(':').last }

5
Je suis un peu confus par cela. Le bloc est utilisé si vous avez besoin de votre propre fonction de comparaison - dans votre exemple, l'envoi uniqà ce tableau sans bloc retournerait la même valeur qu'avec votre bloc.
hdgarrood

18

Juste une autre alternative si quelqu'un s'en soucie.

Vous pouvez également utiliser la to_setméthode d'un tableau qui convertit le tableau en un ensemble et, par définition, les éléments d'ensemble sont uniques.

[1,2,3,4,5,5,5,6].to_set => [1,2,3,4,5,6]

4
Si vous vous souciez de la mémoire, to_setallouera 4 objets, tout en uniqalloue un.
Jan Klimo

18

Si quelqu'un cherchait un moyen de supprimer toutes les instances de valeurs répétées, voir " Comment puis-je extraire efficacement des éléments répétés dans un tableau Ruby? ".

a = [1, 2, 2, 3]
counts = Hash.new(0)
a.each { |v| counts[v] += 1 }
p counts.select { |v, count| count == 1 }.keys # [1, 3]

3
Ou pourrait tout simplement faire a = [1, 2, 2, 3] a.find_all { |x| a.count(x) == 1 } # [1, 3]
Tim Wright

La question liée n'est pas la même; Il s'agit de savoir comment trouver des valeurs dupliquées et les renvoyer. L'OP souhaite supprimer les doublons.
le Tin Man

0

Juste pour donner un aperçu:

require 'fruity'
require 'set'

array = [1,2,2,1,4,4,5,6,7,8,5,6] * 1_000

def mithun_sasidharan(ary)
  ary.uniq
end

def jaredsmith(ary)
  ary & ary
end

def lri(ary)
  counts = Hash.new(0)
  ary.each { |v| counts[v] += 1 }
  counts.select { |v, count| count == 1 }.keys 
end

def finks(ary)
  ary.to_set
end

def santosh_mohanty(ary)
    result = ary.reject.with_index do |ele,index|
      res = (ary[index+1] ^ ele)
      res == 0
    end
end

SHORT_ARRAY = [1,1,2,2,3,1]
mithun_sasidharan(SHORT_ARRAY) # => [1, 2, 3]
jaredsmith(SHORT_ARRAY) # => [1, 2, 3]
lri(SHORT_ARRAY) # => [3]
finks(SHORT_ARRAY) # => #<Set: {1, 2, 3}>
santosh_mohanty(SHORT_ARRAY) # => [1, 2, 3, 1]

puts 'Ruby v%s' % RUBY_VERSION

compare do
  _mithun_sasidharan { mithun_sasidharan(array) }
  _jaredsmith { jaredsmith(array) }
  _lri { lri(array) }
  _finks { finks(array) }
  _santosh_mohanty { santosh_mohanty(array) }
end

Ce qui, une fois exécuté, entraîne:

# >> Ruby v2.7.1
# >> Running each test 16 times. Test will take about 2 seconds.
# >> _mithun_sasidharan is faster than _jaredsmith by 2x ± 0.1
# >> _jaredsmith is faster than _santosh_mohanty by 4x ± 0.1 (results differ: [1, 2, 4, 5, 6, 7, 8] vs [1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, ...
# >> _santosh_mohanty is similar to _lri (results differ: [1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, 7, 8, 5, 6, 1, 2, 1, 4, 5, 6, ...
# >> _lri is similar to _finks (results differ: [] vs #<Set: {1, 2, 4, 5, 6, 7, 8}>)

Remarque: ces résultats sont mauvais:

  • lri(SHORT_ARRAY) # => [3]
  • finks(SHORT_ARRAY) # => #<Set: {1, 2, 3}>
  • santosh_mohanty(SHORT_ARRAY) # => [1, 2, 3, 1]

-4

Essayez d'utiliser l'opérateur XOR, sans utiliser les fonctions intégrées:

a = [3,2,3,2,3,5,6,7].sort!

result = a.reject.with_index do |ele,index|
  res = (a[index+1] ^ ele)
  res == 0
end

print result

Avec des fonctions intégrées:

a = [3,2,3,2,3,5,6,7]

a.uniq

2
Je n'ai pas diminué les votes et je ne sais presque rien de Ruby, mais ce n'est pas non .sort!plus une fonction intégrée?
Carolus
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.