Ответ 1
Массивы в Ruby не имеют метода exists?
. И они получили include?
метод как описано в docs.
Что-то вроде
unless @suggested_horses.include?(horse)
@suggested_horses << horse
end
должен работать из коробки.
У меня есть массив @horses = []
, который я заполняю некоторыми случайными лошадьми.
Как проверить, содержит ли мой массив @horses
лошадь, которая уже включена (существует) в ней?
Я пробовал что-то вроде:
@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
Я также пробовал с include?
, но я видел, что это было только для строк. С exists?
я получаю следующую ошибку:
undefined method `exists?' for #<Array:0xc11c0b8>
Итак, вопрос в том, как я могу проверить, включен ли в моем массиве "лошадь", чтобы я не заполнял его той же лошадью?
Массивы в Ruby не имеют метода exists?
. И они получили include?
метод как описано в docs.
Что-то вроде
unless @suggested_horses.include?(horse)
@suggested_horses << horse
end
должен работать из коробки.
Если вы хотите проверить, находится ли объект внутри массива, проверяя атрибут объекта, вы можете использовать any?
и передать блок, который оценивает значение true или false:
unless @suggested_horses.any? {|h| h.id == horse.id }
@suggested_horses << horse
end
Почему бы просто не выбрать восемь разных чисел от 0
до Horse.count
и использовать их для получения ваших лошадей?
offsets = (0...Horse.count).to_a.sample(8)
@suggested_horses = offsets.map{|i| Horse.first(:offset => i) }
У этого есть дополнительное преимущество, что он не вызовет бесконечный цикл, если в вашей базе данных будет меньше 8 лошадей.
Примечание: Array#sample
является новым для 1.9 (и в 1.8.8), поэтому либо обновите Ruby, require 'backports'
, либо используйте что-то вроде shuffle.first(n)
.
#include?
должен работать, он работает для общих объектов, а не только строк. Ваша проблема в примере кода - это тест:
unless @suggested_horses.exists?(horse.id)
@suggested_horses<< horse
end
(даже если использовать #include?
). Вы пытаетесь выполнить поиск определенного объекта, а не идентификатора. Так оно и должно быть:
unless @suggested_horses.include?(horse)
@suggested_horses << horse
end
ActiveRecord имеет redefined оператор сравнения для объектов, чтобы посмотреть только его состояние (новое/созданное) и id
Метод Array include?
принимает любой объект, а не только строку. Это должно работать:
@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
Итак, вопрос в том, как я могу проверить, включен ли в моем массиве "лошадь", чтобы я не заполнял его той же лошадью?
В то время как ответы связаны с просмотром массива, чтобы увидеть, существует ли конкретная строка или объект, действительно ли это происходит неправильно, потому что по мере увеличения массива поиск займет больше времени.
Вместо этого используйте либо Hash, либо Set. Оба позволяют только один экземпляр определенного элемента. Набор будет вести себя ближе к массиву, но допускает только один экземпляр. Это более превентивный подход, который позволяет избежать дублирования из-за характера контейнера.
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 используются пары имя/значение, что означает, что значения не будут иметь реального использования, но, похоже, немного больше скорости, используя Hash, на основе некоторых тестов.
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
Какие выходы:
user system total real
Hash 8.140000 0.130000 8.270000 ( 8.279462)
Array 10.680000 0.120000 10.800000 ( 10.813385)
Это...
horse = Horse.find(:first,:offset=>rand(Horse.count))
unless @suggested_horses.exists?(horse.id)
@suggested_horses<< horse
end
Возможно, это будет...
horse = Horse.find(:first,:offset=>rand(Horse.count))
unless @suggested_horses.include?(horse)
@suggested_horses<< horse
end