How to check if my array contains an object?

I have an array @horses = [] , which I fill with some random horses.

How to check if my @horses array @horses horse that is already included (exists) in it?

I tried something like:

 @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 

I also tried with include? but I saw that it was only for strings. With exists? I get the following error:

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

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

+64
ruby ruby-on-rails
Jul 27 '10 at 13:01
source share
7 answers

Arrays in Ruby don't have an exists? method exists? . And did they get include? method as described in docs . Something like

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

should work out of the box.

+154
Jul 27 '10 at 13:13
source share

If you want to check if an object is inside an array by checking the attribute of an object, can you use any? and pass a block that evaluates to true or false:

 unless @suggested_horses.any? {|h| h.id == horse.id } @suggested_horses << horse end 
+10
Sep 25 '15 at 19:48
source share

Why not just pick eight different numbers from 0 to Horse.count and use them to get your horses?

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

This has the added benefit that it will not cause an infinite loop if there are less than 8 horses in your database.

Note: Array#sample is new to 1.9 (and in 1.8.8), so either upgrade Ruby, require 'backports' , or use something like shuffle.first(n) .

+3
Jul 27 '10 at 20:40
source share

#include? should work, it works for shared objects , not just strings. Your problem in the sample code is the test:

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

(even if you use #include? ). You are trying to search for a specific object, not an identifier. So it should be:

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

ActiveRecord has a redefined comparison operator for objects to see only its state (new / created) and id

+2
Jul 27 '10 at 13:13
source share

Array include? method include? accepts any object, not just a string. This should work:

 @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 
+1
Jul 27 '10 at 13:20
source share

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

While the answers are related to looking at the array to see if a particular row or object exists, is this really happening incorrectly, because as the array grows, the search will take longer.

Use either Hash or Set instead. Both allow only one instance of a specific item. The set will behave closer to the array, but allows only one instance. This is a more proactive approach that avoids duplication due to the nature of the container.

 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 uses name / value pairs, which means the values ​​will not have real use, but it seems to be a bit more speed using Hash, based on some tests.

 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 

What outputs:

  user system total real Hash 8.140000 0.130000 8.270000 ( 8.279462) Array 10.680000 0.120000 10.800000 ( 10.813385) 
+1
Jun 29 '13 at 5:01
source share

It...

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

Perhaps it will be ...

 horse = Horse.find(:first,:offset=>rand(Horse.count)) unless @suggested_horses.include?(horse) @suggested_horses<< horse end 
0
Jul 27 '10 at 13:11
source share



All Articles