How do I check to see if my array includes an object?

后端 未结 7 1870
刺人心
刺人心 2020-12-04 23:19

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

How can I check if my @horses array includes a horse that is already incl

相关标签:
7条回答
  • 2020-12-04 23:31

    #include? should work, it works for general objects, not only strings. Your problem in example code is this test:

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

    (even assuming using #include?). You try to search for specific object, not for id. So it should be like this:

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

    ActiveRecord has redefined comparision operator for objects to take a look only for its state (new/created) and id

    0 讨论(0)
  • 2020-12-04 23:40

    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?

    While the answers are concerned with looking through the array to see if a particular string or object exists, that's really going about it wrong, because, as the array gets larger, the search will take longer.

    Instead, use either a Hash, or a Set. Both only allow a single instance of a particular element. Set will behave closer to an Array but only allows a single instance. This is a more preemptive approach which avoids duplication because of 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 won't be of any real use, but there seems to be a little bit of extra speed using a 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
    

    Which outputs:

                user     system      total        real
    Hash    8.140000   0.130000   8.270000 (  8.279462)
    Array  10.680000   0.120000  10.800000 ( 10.813385)
    
    0 讨论(0)
  • 2020-12-04 23:45

    Why not do it simply by picking eight different numbers from 0 to Horse.count and use that 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 advantage that it won't cause an infinite loop if you happen to have less than 8 horses in your database.

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

    0 讨论(0)
  • 2020-12-04 23:46

    This ...

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

    Should probably be this ...

    horse = Horse.find(:first,:offset=>rand(Horse.count))
    unless @suggested_horses.include?(horse)
       @suggested_horses<< horse
    end
    
    0 讨论(0)
  • 2020-12-04 23:48

    If you want to check if an object is within in array by checking an attribute on the object, you can 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
    
    0 讨论(0)
  • 2020-12-04 23:50

    Arrays in Ruby don't have exists? method, but they have an include? method as described in the docs. Something like

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

    should work out of box.

    0 讨论(0)
提交回复
热议问题