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


77

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

因此,问题是如何检查阵列中是否已包含“马”,这样我才不会用同一匹马填充它?


如果未使用Python来表达该问题,则该问题将是stackoverflow.com/questions/1529986/…的副本。
Dave Schweisguth'7

Answers:


177

Ruby中的数组没有exists?方法,但是它们具有docs中描述include?方法。就像是

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

应该开箱即用。


1
这可能是不好的,因为包括?将扫描整个数组,并且为n O(n)的顺序
Kush 2014年

8
时间是线性的,在每个项目中都会在列表中浏览一次。不过,仔细评估性能没有任何意义。这是一个简单的案例。如果您关心快速查找,请使用HashSet从中查找std-lib
TomaszCudziło2014年

3
反之:除非horse.in?(@suggested_horses)@suggested_horses <<马尾
Felix Livni

2
@库什:同意,O(n)并不理想。但是,如果我们发生短路,则平均情况仍然是O(n);)最后,我们选择用Ruby(所有语言中的...)编写
Mike Rapadas

24

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

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

3

为什么不简单地从0to中选择八个不同的数字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中推出),因此请升级Rubyrequire 'backports'或使用shuffle.first(n)


2

#include?应该起作用,它适用于一般对象,而不仅适用于字符串。示例代码中的问题是此测试:

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

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

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

ActiveRecord为对象重新定义了比较运算符,以便仅查看其状态(新的/创建的)和ID


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

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)

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.