Eliminar elementos duplicados de la matriz en Ruby

325

Tengo una matriz Ruby que contiene elementos duplicados.

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

¿Cómo puedo eliminar todos los elementos duplicados de esta matriz y al mismo tiempo conservar todos los elementos únicos sin usar for-loops e iteraciones?

Mithun Sasidharan
fuente

Respuestas:

722
array = array.uniq

uniq elimina todos los elementos duplicados y retiene todos los elementos únicos en la matriz.

Esta es una de las muchas bellezas del lenguaje Ruby.

Mithun Sasidharan
fuente
50
no, el uniq! El método devolverá nil si la matriz ha sido única aún. Ej: a = [1,2,3,4] a.uniq -> [1,2,3,4] pero a.uniq! -> nil
duykhoa
15
realmente no vería esto como una belleza del lenguaje ruby ​​... ¿es solo la belleza de la biblioteca estándar ruby? no me malinterpreten, hay muchas cosas hermosas sobre el idioma.
Justin L.
77
escribe lo mismo en Objective-C, Javascript y PHP. ¡Entonces dinos que Ruby no es un lenguaje hermoso!
Adam Waite
3
Esto también funciona para tipos complejos: [{how: "are"}, {u:"doing"}, {how: "are"}].uniq => [{:how=>"are"}, {:u=>"doing"}]
Blaskovicz
55
sobre lo que dice @duykhoa, el uniq! devuelve el método nulo, pero por lo general no se preocupan por el regreso de un .uniq!que hace el trabajo sobre el objeto en sí mismo
carpinchosaurio
82

Puedes devolver la intersección.

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

Esto también eliminará duplicados.

jaredsmith
fuente
12
Funcionalmente, esta respuesta es correcta, pero creo que esto es notablemente menos legible que solo usar uniq.
Fiona T
21
Solo lo estaba poniendo aquí para que quien visite esta página vea otras formas de hacerlo también, no estaba tratando de decir que es mejor de ninguna manera.
jaredsmith
3
La razón por la que esto funciona es porque cuando se usan operaciones de conjunto, la matriz resultante se trata como un conjunto, que es una estructura de datos que generalmente no tiene valores repetidos. Usar a | a(union) haría el mismo truco.
Cezar
47

Puede eliminar los elementos duplicados con el método uniq:

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

Lo que también podría ser útil saber es que uniqtoma un bloque, por lo que si tiene un conjunto de claves:

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

y desea saber cuáles son los archivos únicos, puede averiguarlo con:

a.uniq { |f| f[/\d+$/] }.map { |p| p.split(':').last }
Marek Příhoda
fuente
55
Estoy un poco confundido por esto. El bloque se usa si necesita su propia función de comparación; en su ejemplo, enviar uniqa esa matriz sin un bloque devolvería el mismo valor que con su bloque.
hdgarrood
18

Solo otra alternativa si a alguien le importa.

También puede usar el to_setmétodo de una matriz que convierte la matriz en un conjunto y, por definición, los elementos del conjunto son únicos.

[1,2,3,4,5,5,5,6].to_set => [1,2,3,4,5,6]
Finks
fuente
44
Si le importa la memoria, to_setasignará 4 objetos, mientras que uniqasigna uno.
Jan Klimo
18

Si alguien buscaba una forma de eliminar todas las instancias de valores repetidos, consulte " ¿Cómo puedo extraer elementos repetidos de manera eficiente en una matriz 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
fuente
3
O simplemente podría hacerlo a = [1, 2, 2, 3] a.find_all { |x| a.count(x) == 1 } # [1, 3]
Tim Wright
La pregunta vinculada no es la misma; Se pregunta cómo encontrar valores duplicados y devolverlos. El OP quiere eliminar duplicados.
El hombre de hojalata
0

Solo para proporcionar una idea:

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

Lo cual, cuando se ejecuta, da como resultado:

# >> 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}>)

Nota: estos arrojaron malos resultados:

  • lri(SHORT_ARRAY) # => [3]
  • finks(SHORT_ARRAY) # => #<Set: {1, 2, 3}>
  • santosh_mohanty(SHORT_ARRAY) # => [1, 2, 3, 1]
el hombre de hojalata
fuente
-4

Intente usar el operador XOR, sin usar las funciones integradas:

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

Con funciones integradas:

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

a.uniq
Santosh Mohanty
fuente
2
No he votado negativamente y no sé casi nada sobre Ruby, pero ¿no es .sort!también una función incorporada?
Carolus