如何检查我的数组是否包含对象?

时间:2010-07-27 13:01:20

标签: ruby-on-rails ruby

我有一个数组@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>

所以问题是如何检查我的阵列是否已经包含了“马”,以便我不会用同一匹马填充它?

7 个答案:

答案 0 :(得分:170)

Ruby中的数组没有exists?方法,但它们有include?方法as described in the docs。 像

这样的东西
unless @suggested_horses.include?(horse)
   @suggested_horses << horse
end

应该开箱即用。

答案 1 :(得分:13)

如果要通过检查对象上的属性来检查对象是否在数组中,可以使用any?并传递一个计算结果为true或false的块:

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

答案 2 :(得分:3)

为什么不通过从0Horse.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)之类的内容

答案 3 :(得分:2)

#include?应该有用,它适用于general objects,而不仅仅是字符串。您在示例代码中的问题是此测试:

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

(即使假设使用#include?)。您尝试搜索特定对象,而不是id。所以它应该是这样的:

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

ActiveRecord有redefined比较运算符,用于查看对象的状态(new / created)和id

答案 4 :(得分:1)

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

答案 5 :(得分:1)

  

所以问题是如何检查我的阵列是否已经包含了“马”,以便我不会用同一匹马填充它?

虽然答案涉及通过查看数组以查看特定字符串或对象是否存在,但这确实是错误的,因为随着数组变大,搜索将花费更长时间。

相反,请使用HashSet。两者都只允许特定元素的单个实例。 Set将表现得更接近Array,但只允许单个实例。这是一种更具先发制人的方法,由于容器的性质,避免了重复。

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"}>

哈希使用名称/值对,这意味着这些值不会有任何实际用途,但基于某些测试,使用哈希似乎有一点额外的速度。

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)

答案 6 :(得分:0)

这......

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