arr
es una variedad de cadenas:
["hello", "world", "stack", "overflow", "hello", "again"]
¿Cuál sería una manera fácil y elegante de verificar si arr
tiene duplicados y, de ser así, devolver uno de ellos (sin importar cuál)?
Ejemplos:
["A", "B", "C", "B", "A"] # => "A" or "B"
["A", "B", "C"] # => nil
arr == arr.uniq
sería una manera fácil y elegante de verificar siarr
tiene duplicados, sin embargo, no proporciona cuáles fueron duplicados.Respuestas:
Sé que esta no es una respuesta muy elegante, pero me encanta. Es hermoso un código de línea. Y funciona perfectamente bien a menos que necesite procesar un gran conjunto de datos.
¿Busca una solución más rápida? ¡Aqui tienes!
Es lineal, O (n), pero ahora necesita administrar múltiples líneas de código, necesita casos de prueba, etc.
Si necesita una solución aún más rápida, pruebe con C en su lugar.
Y aquí está la esencia de comparar diferentes soluciones: https://gist.github.com/naveed-ahmad/8f0b926ffccf5fbd206a1cc58ce9743e
fuente
a.select {|e| a.count(e) > 1}.uniq
Puede hacerlo de varias maneras, siendo la primera opción la más rápida:
Y una opción O (N ^ 2) (es decir, menos eficiente):
fuente
group_by.select
ary.group_by(&:itself)
. :-)Simplemente encuentre la primera instancia donde el índice del objeto (contando desde la izquierda) no es igual al índice del objeto (contando desde la derecha).
Si no hay duplicados, el valor de retorno será nulo.
Creo que esta es la solución más rápida publicada en el hilo hasta ahora, ya que no se basa en la creación de objetos adicionales
#index
y#rindex
se implementa en C. El tiempo de ejecución de Big-O es N ^ 2 y, por lo tanto, más lento que Sergio, pero el tiempo de la pared podría ser mucho más rápido debido al hecho de que las partes "lentas" corren en C.fuente
arr.find_all {|e| arr.rindex(e) != arr.index(e) }.uniq
arr.detect.with_index { |e, idx| idx != arr.rindex(e) }
. El usowith_index
debería eliminar la necesidad de la primeraindex
búsqueda.detect
solo encuentra un duplicado.find_all
los encontrará a todos:fuente
count
para cada elemento de la matriz. (Un hash de conteo, por ejemplo, es mucho más eficiente; por ejemplo, construirh = {"A"=>2, "B"=>2, "C"=> 1 }
entoncesh.select { |k,v| v > 1 }.keys #=> ["A", "B"]
.Aquí hay dos formas más de encontrar un duplicado.
Usar un conjunto
Usar
select
en lugar defind
para devolver una matriz de todos los duplicados.Utilizar
Array#difference
soltar
.first
para devolver una matriz de todos los duplicados.Ambos métodos regresan
nil
si no hay duplicados.I propuse que
Array#difference
ser añadido al núcleo Ruby. Más información está en mi respuesta aquí .Punto de referencia
Comparemos los métodos sugeridos. Primero, necesitamos una matriz para probar:
y un método para ejecutar los puntos de referencia para diferentes matrices de prueba:
No incluí la respuesta de @ JjP porque solo se debe devolver un duplicado, y cuando se modifica su respuesta para hacerlo, es lo mismo que la respuesta anterior de @ Naveed. Tampoco incluí la respuesta de @ Marin, que, aunque se publicó antes de la respuesta de @ Naveed, devolvió todos los duplicados en lugar de solo uno (un punto menor, pero no hay ningún punto para evaluar ambos, ya que son idénticos cuando devuelven solo un duplicado).
También modifiqué otras respuestas que devolvieron todos los duplicados para devolver solo el primero encontrado, pero que esencialmente no debería tener ningún efecto en el rendimiento, ya que calcularon todos los duplicados antes de seleccionar uno.
Los resultados para cada punto de referencia se enumeran del más rápido al más lento:
Primero suponga que la matriz contiene 100 elementos:
Ahora considere una matriz con 10,000 elementos:
Tenga en cuenta que
find_a_dup_using_difference(arr)
sería mucho más eficiente siArray#difference
se implementara en C, que sería el caso si se agregara al núcleo de Ruby.Conclusión
Muchas de las respuestas son razonables, pero usar un Set es la mejor opción . Es más rápido en los casos de dureza media, la unión más rápida en los casos más difíciles y solo computacionalmente triviales, cuando su elección no importará de todos modos, puede ser vencido.
El único caso muy especial en el que podría elegir la solución de Chris sería si desea utilizar el método para desduplicar por separado miles de matrices pequeñas y espera encontrar un duplicado que generalmente contiene menos de 10 elementos. Esto será un poco más rápido ya que evita la pequeña sobrecarga adicional de crear el Conjunto.
fuente
Por desgracia, la mayoría de las respuestas son
O(n^2)
.Aquí hay una
O(n)
solución,¿Cuál es la complejidad de esto?
O(n)
y se rompe en el primer partidoO(n)
memoria, pero solo la cantidad mínimaAhora, dependiendo de la frecuencia con la que haya duplicados en su matriz, estos tiempos de ejecución podrían ser aún mejores. Por ejemplo, si la matriz de tamaño
O(n)
se ha muestreado de una población dek << n
elementos diferentes, solo se vuelve la complejidad tanto para el tiempo de ejecución como para el espacioO(k)
, sin embargo, es más probable que el póster original esté validando la entrada y quiera asegurarse de que no haya duplicados. En ese caso, tanto el tiempo de ejecución como la complejidad de la memoriaO(n)
ya que esperamos que los elementos no tengan repeticiones para la mayoría de las entradas.fuente
Objetos Array rubí tienen un gran método,
select
.La primera forma es lo que te interesa aquí. Le permite seleccionar objetos que pasan una prueba.
Objetos Array de Ruby tienen otro método,
count
.En este caso, le interesan los duplicados (objetos que aparecen más de una vez en la matriz). La prueba adecuada es
a.count(obj) > 1
.Si
a = ["A", "B", "C", "B", "A"]
, entoncesUsted declara que solo quiere un objeto. Así que elige uno.
fuente
["A", "B", "B", "A"]
.uniq
en la matriz.count
para cada elemento de la matriz, lo cual es un desperdicio e innecesario. Vea mi comentario sobre la respuesta de JjP.find_all () devuelve un que
array
contiene todos los elementos de loenum
queblock
no esfalse
.Para obtener
duplicate
elementosO
uniq
elementos duplicadosfuente
Algo como esto funcionará
Es decir, coloque todos los valores en un hash donde la clave es el elemento de la matriz y el valor es el número de ocurrencias. Luego seleccione todos los elementos que ocurran más de una vez. Fácil.
fuente
Sé que este hilo trata específicamente de Ruby, pero llegué aquí buscando cómo hacer esto dentro del contexto de Ruby on Rails con ActiveRecord y pensé que también compartiría mi solución.
Lo anterior devuelve una matriz de todas las direcciones de correo electrónico que están duplicadas en la tabla de base de datos de este ejemplo (que en Rails sería "active_record_classes").
fuente
Este es un
O(n)
procedimiento.Alternativamente, puede hacer cualquiera de las siguientes líneas. También O (n) pero solo una iteración
fuente
Aquí está mi opinión sobre un gran conjunto de datos, como una tabla de dBase heredada para encontrar partes duplicadas
fuente
fuente
each_with_object
¡es tu amigo!fuente
Este código devolverá la lista de valores duplicados. Las claves hash se utilizan como una forma eficiente de verificar qué valores ya se han visto. Según si se ha visto el valor, la matriz original
ary
se divide en 2 matrices: la primera contiene valores únicos y la segunda contiene duplicados.Puede acortarlo aún más, aunque a costa de una sintaxis un poco más compleja, de esta forma:
fuente
Resultados
fuente
Si está comparando dos matrices diferentes (en lugar de una contra sí misma), una forma muy rápida es utilizar el operador de intersección
&
proporcionado por la clase Ruby's Array .fuente
Necesitaba averiguar cuántos duplicados había y cuáles eran, así que escribí una función basada en lo que Naveed había publicado anteriormente:
fuente
demostremos en Implementación de Código
Ahora llame al método de duplicación y al resultado de retorno de salida:
fuente
[1,2,3].uniq!.nil? => true
[1,2,3,3].uniq!.nil? => false
Note que lo anterior es destructivo
fuente