2010-07-27 14 views
65

Tengo una matriz @horses = [] que llené con algunos caballos al azar.¿Cómo verifico si mi matriz incluye un objeto?

¿Cómo puedo verificar si mi matriz @horses incluye un caballo que ya está incluido (existe) en ella?

he intentado algo así como:

@suggested_horses = [] 
    @suggested_horses << Horse.find(:first,:offset=>rand(Horse.count)) 
    while @suggested_horses.length < 8 
    horse = Horse.find(:first,:offset=>rand(Horse.count)) 
    unless @suggested_horses.exists?(horse.id) 
     @suggested_horses<< horse 
    end 
    end 

También probé con include? pero vi que era de sólo cadenas. Con exists? me sale el siguiente error:

undefined method `exists?' for #<Array:0xc11c0b8> 

Así que la pregunta es ¿cómo puedo comprobar si mi serie ya tiene un "caballo" incluido para que yo no lo llene con el mismo caballo?

+0

Esta pregunta sería un duplicado de https://stackoverflow.com/questions/1529986/ruby-methods-equivalent-of-if-a-in-list-in-python si esa pregunta weren redactado en términos de Python. –

Respuesta

156

matrices en Ruby no tienen exists? método. Y tienen include? método as described in docs. Algo así como

unless @suggested_horses.include?(horse) 
    @suggested_horses << horse 
end 

debe trabajar fuera de la caja.

+1

esto podría ser malo, ya que incluir? escaneará toda la matriz y ordenará n O (n) – Kush

+6

Es lineal en el tiempo, recorre la lista una vez sobre cada elemento. Sin embargo, no tiene ningún sentido criticar el rendimiento. Es un caso tan simple. Si le importa la búsqueda rápida, use ['Hash'] (http://www.ruby-doc.org/core-2.1.1/Hash.html) o [' Set'] (http: //www.ruby). -doc.org/stdlib-2.1.1/libdoc/set/rdoc/Set.html) de 'std-lib'. –

+2

Inversamente: menos horse.in?(@suggested_horses) @suggested_horses << caballo finales –

0

Este ...

horse = Horse.find(:first,:offset=>rand(Horse.count)) 
unless @suggested_horses.exists?(horse.id) 
    @suggested_horses<< horse 
end 

probablemente debería ser esto ...

horse = Horse.find(:first,:offset=>rand(Horse.count)) 
unless @suggested_horses.include?(horse) 
    @suggested_horses<< horse 
end 
2

#include? debería funcionar, funciona para general objects, no sólo cadenas. Su problema en el ejemplo de código es la siguiente prueba:

unless @suggested_horses.exists?(horse.id) 
    @suggested_horses<< horse 
end 

(incluso asumiendo usando #include?). Intenta buscar un objeto específico, no para el id. Por lo que debe ser como este:

unless @suggested_horses.include?(horse) 
    @suggested_horses << horse 
end 

ActiveRecord tiene redefined operador de comparación para los objetos que echar un vistazo sólo por su estado (nueva/creado) y la identificación

1

El método include? de la matriz acepta cualquier objeto, no solo una cadena. Esto debería funcionar:

@suggested_horses = [] 
@suggested_horses << Horse.first(:offset => rand(Horse.count)) 
while @suggested_horses.length < 8 
    horse = Horse.first(:offset => rand(Horse.count)) 
    @suggested_horses << horse unless @suggested_horses.include?(horse) 
end 
3

¿Por qué no simplemente escogiendo ocho números diferentes de 0 a Horse.count y usarlo para obtener sus caballos?

offsets = (0...Horse.count).to_a.sample(8) 
@suggested_horses = offsets.map{|i| Horse.first(:offset => i) } 

Esto tiene la ventaja añadida de que no causará un bucle infinito si le sucede que tiene menos de 8 caballos en su base de datos.

Nota:Array#sample es nuevo para 1.9 (y que viene en 1.8.8), así que o actualizar su Ruby, require 'backports' o usar algo como shuffle.first(n).

1

So the question is how can I check if my array already has a "horse" included so that I don't fill it with the same horse?

Mientras que las respuestas tienen que ver con mirar a través de la matriz para ver si existe una cadena o un objeto particular que está pasando realmente mal al respecto, ya que, como la matriz se hace más grande, la búsqueda tardará más tiempo.

En su lugar, use Hash o Set. Ambos solo permiten una instancia única de un elemento en particular. Set se comportará más cerca de una matriz pero solo permite una sola instancia. Este es un enfoque más preventivo que evita la duplicación debido a la naturaleza del contenedor.

hash = {} 
hash['a'] = nil 
hash['b'] = nil 
hash # => {"a"=>nil, "b"=>nil} 
hash['a'] = nil 
hash # => {"a"=>nil, "b"=>nil} 

require 'set' 
ary = [].to_set 
ary << 'a' 
ary << 'b' 
ary # => #<Set: {"a", "b"}> 
ary << 'a' 
ary # => #<Set: {"a", "b"}> 

Hash utiliza pares de nombre/valor, lo que significa los valores no serán de alguna utilidad real, pero parece que hay un poco de velocidad extra usando un hash, en base a unas pruebas.

require 'benchmark' 
require 'set' 

ALPHABET = ('a' .. 'z').to_a 
N = 100_000 
Benchmark.bm(5) do |x| 
    x.report('Hash') { 
    N.times { 
     h = {} 
     ALPHABET.each { |i| 
     h[i] = nil 
     } 
    } 
    } 

    x.report('Array') { 
    N.times { 
     a = Set.new 
     ALPHABET.each { |i| 
     a << i 
     } 
    } 
    } 
end 

que da salida:

  user  system  total  real 
Hash 8.140000 0.130000 8.270000 ( 8.279462) 
Array 10.680000 0.120000 10.800000 (10.813385) 
10

Si desea comprobar si un objeto está dentro de una matriz mediante una comprobación de un atributo en el objeto, puede utilizar any? y pasar un bloque que se evalúa como verdadera o falso:

unless @suggested_horses.any? {|h| h.id == horse.id } 
    @suggested_horses << horse 
end 
Cuestiones relacionadas