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?

Mithun Sasidharan
la source

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.

Mithun Sasidharan
la source
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.

jaredsmith
la source
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 }
Marek Příhoda
la source
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]
Finks
la source
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]
Lri
la source
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]
l'homme d'étain
la source
-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
Santosh Mohanty
la source
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