Agregue un elemento a una matriz si aún no está allí

92

Tengo una clase ruby

class MyClass
  attr_writer :item1, :item2
end

my_array = get_array_of_my_class() #my_array is an array of MyClass
unique_array_of_item1 = []

Quiero empujar MyClass#item1a unique_array_of_item1, pero sólo si unique_array_of_item1no contiene que item1todavía. Hay una solución simple que conozco: simplemente iterar my_arrayy verificar si unique_array_of_item1ya contiene la corriente item1o no.

¿Existe alguna solución más eficiente?

Alan Coromano
fuente

Respuestas:

82

Puede utilizar Set en lugar de Array.

Jiří Pospíšil
fuente
Si bien es cierto que los documentos dicen que los conjuntos no están ordenados, de hecho están ordenados (a partir de Ruby 1.9). Si observa el código, los métodos principales que usaría para obtener la orden (como Set#eachy Set#to_a) delegar @hash. Y a partir de Ruby 1.9 se ordenan los Hashes. "Los hash enumeran sus valores en el orden en que se insertaron las claves correspondientes". ruby-doc.org/core-1.9.1/Hash.html
Phylae
Nunca fue nuevo que existiera un conjunto. Son increíbles, muchas gracias
Brad
123

@Coorasse tiene una buena respuesta , aunque debería ser:

my_array | [item]

Y para actualizar my_arrayen su lugar:

my_array |= [item]
Jason Denney
fuente
63
o my_array |= [item]que se actualizará my_arrayen su lugar
andorov
2
Quizás me falta algo aquí, pero el operador | = no parece funcionar para mí. Estoy ejecutando Ruby 2.1.1
Viet
@Viet |=funciona bien en mis pruebas con 2.1.1. Describe tu caso de prueba o abre una nueva pregunta.
depquid
Probándolo de nuevo y funciona ahora. No sé qué estaba haciendo antes, ya que hice mi comentario hace muchos meses.
Viet
1
¿Cuál es la complejidad de esto?
Nobita
40

No es necesario que lo recorras a my_arraymano.

my_array.push(item1) unless my_array.include?(item1)

Editar:

Como señala Tombart en su comentario, usar Array#include?no es muy eficiente. Yo diría que el impacto en el rendimiento es insignificante para las matrices pequeñas, pero es posible que desee optar Setpor las más grandes.

doesterr
fuente
6
¡definitivamente no quieres hacer eso! array.include?(item)tiene complejidad O(n), por lo que es como iterar toda la matriz. Eche un vistazo a este punto de referencia: gist.github.com/deric/4953652
Tombart
32

Puede convertir item1 en matriz y unirlos:

my_array | [item1]
coorasse
fuente
1
Esto debería ser |no ||(véase la respuesta de Jason)
Seth
1
Mi culpa. Lo siento.
Editó
3

Es importante tener en cuenta que la clase Set y la | El método (también llamado "Establecer unión") producirá una matriz de elementos únicos , lo cual es excelente si no desea duplicados, pero será una sorpresa desagradable si tiene elementos no únicos en su matriz original por diseño.

Si tiene al menos un elemento duplicado en su matriz original que no quiere perder, iterar a través de la matriz con un retorno temprano es el peor de los casos O (n), que no es tan malo en el gran esquema de las cosas .

class Array
  def add_if_unique element
    return self if include? element
    push element
  end
end
elreimundo
fuente
0

No estoy seguro de si es la solución perfecta, pero funcionó para mí:

    host_group = Array.new if not host_group.kind_of?(Array)
    host_group.push(host)
witkacy26
fuente