ChatGPT解决这个技术问题 Extra ChatGPT

如何检查一个值是否存在于Ruby中的数组中

我有一个值 'Dog' 和一个数组 ['Cat', 'Dog', 'Bird']

如何在不循环遍历的情况下检查它是否存在于数组中?有没有一种简单的方法来检查值是否存在,仅此而已?

使用 .include? method。它返回一个你想要的布尔值。在您的情况下,只需键入: ['Cat', 'Dog', 'Bird'].include('Dog') 它应该返回布尔值 true。
如果您想多次检查数组中是否存在不同的值,请不要使用 include? 方法,因为包含?每次都将遍历数组,每次进行 O(n) 操作进行搜索,而不是做一个哈希 hash = arr.map {|x| [x,true]}.to_h,现在检查 hash.has_key? 'Dog' 是否返回 true
你真的不能完全“不循环”地做到这一点。这在逻辑上是不可能的,计算机无法确定数组是否包含该元素,而无需遍历元素以检查它们中的任何一个是否是它正在搜索的元素。当然,除非它是空的。那我猜你不需要循环。
请参阅下面的基准测试,以测试在 Array 和 Set 中查找元素的各种方法的差异。 stackoverflow.com/a/60404934/128421

A
Arslan Ali

您正在寻找 include?

>> ['Cat', 'Dog', 'Bird'].include? 'Dog'
=> true

替代语法:%w(Cat Dog Bird).include? 'Dog'
有时我希望它是“包含”而不是包含。我总是把它和包含混在一起。
请注意,在内部,#include? 仍然执行循环。不过,编码器免于显式编写循环。我添加了一个真正执行任务而不循环的答案。
@HenleyChiu I,它被称为 [ 'Dog', 'Bird', 'Cat' ].has? 'Dog'
@AlfonsoVergara 是的,数组的任何解决方案都必须在内部进行某种循环;没有循环就无法测试数组的成员资格。如果您不想在内部进行任何循环,则需要使用不同的数据结构,例如具有固定大小键的完美哈希表。鉴于没有内部循环就无法测试数组中的成员资格,我将问题解释为“无需自己明确编写循环”
N
Nick

正如@campaterson 所指出的,自 v3.1 以来,ActiveSupport(Rails 的一部分)中有一个 in? method。所以在 Rails 中,或者如果您 require 'active_support',您可以编写:

'Unicorn'.in?(['Cat', 'Dog', 'Bird']) # => false

OTOH,Ruby 本身没有 in 运算符或 #in? 方法,尽管它之前已经提出,但 in particular by Yusuke Endoh 是 ruby-core 的顶级成员。

正如其他人所指出的,对于包括 ArrayHashSetRange 在内的所有 Enumerable,都存在反向方法 include?

['Cat', 'Dog', 'Bird'].include?('Unicorn') # => false

请注意,如果您的数组中有许多值,它们将一个接一个地被检查(即 O(n)),而查找散列将是恒定时间(即 O(1))。因此,例如,如果您的数组是常量,则最好使用 Set。例如:

require 'set'
ALLOWED_METHODS = Set[:to_s, :to_i, :upcase, :downcase
                       # etc
                     ]

def foo(what)
  raise "Not allowed" unless ALLOWED_METHODS.include?(what.to_sym)
  bar.send(what)
end

quick test 表明在 10 元素 Set 上调用 include? 比在等效的 Array 上调用它快大约 3.5 倍(如果未找到该元素)。

最后的结束说明:在 Range 上使用 include? 时要小心,有一些微妙之处,因此请参阅 the doc 并与 cover? 进行比较...


虽然 Ruby 在其核心中不包含 #in?,但如果您使用的是 Rails,它是可用的。 api.rubyonrails.org/classes/Object.html#method-i-in-3F(我知道这是一个 Ruby,而不是 Rails 问题,但它可能对希望在 Rails 中使用 #in? 的任何人有所帮助。看起来它是在 Rails 3.1 中添加的apidock.com/rails/Object/in%3F
特别感谢您对这样一个事实的评论,即提问者的原点是“不循环通过它”来确定数组中是否存在某些东西是不可能的,即使循环可能隐藏在一个简单的方法调用后面,例如 {1 } -- 以及如何实际上避免这种情况(使用 Set),如果这很重要的话。这个答案得到了我的支持,尽管我可能在最后留下了关于 in? 的部分。
s
schmitzelburger

尝试

['Cat', 'Dog', 'Bird'].include?('Dog')

这是较旧的语法,请看 ^^^ @brian 的答案
@jahrichie 您在这个答案中究竟认为“旧语法”是什么,可选括号?
我同意@Dennis,这不是旧的,括号是可选的,在大多数情况下是一个好的做法....尝试在一行中使用不带括号的包含,例如,我的意思是根据你的情况你应该或必须使用方括号(与“旧”ruby 语法完全无关)
这是我可以在三元运算中使用的唯一语法。
t
the Tin Man

如果要按块检查,可以尝试 any?all?

%w{ant bear cat}.any? {|word| word.length >= 3}   #=> true  
%w{ant bear cat}.any? {|word| word.length >= 4}   #=> true  
[ nil, true, 99 ].any?                            #=> true  

有关详细信息,请参阅 Enumerable

我的灵感来自“evaluate if array has any items in ruby


如果您想检查任何/所有这些字符串是否包含在另一个字符串/常量中,这非常有用
D
DigitalRoss

使用 Enumerable#include

a = %w/Cat Dog Bird/

a.include? 'Dog'

或者,如果完成了许多测试,1 您可以摆脱循环(甚至 include? 也有)并从 O(n) 转到 O(1) 与:

h = Hash[[a, a].transpose]
h['Dog']

1. 我希望这是显而易见的,但要避免反对:是的,对于一些查找,Hash[] 和转置操作在配置文件中占主导地位,并且每个都是 O(n) 本身。


t
the Tin Man

Ruby 有十一种方法来查找数组中的元素。

首选的是 include?,或者,对于重复访问,创建一个 Set,然后调用 include?member?

以下是所有这些:

array.include?(element) # preferred method
array.member?(element)
array.to_set.include?(element)
array.to_set.member?(element)
array.index(element) > 0
array.find_index(element) > 0
array.index { |each| each == element } > 0
array.find_index { |each| each == element } > 0
array.any? { |each| each == element }
array.find { |each| each == element } != nil
array.detect { |each| each == element } != nil

如果元素存在,它们都会返回一个 trueish 值。

include? 是首选方法。它在内部使用 C 语言 for 循环,当元素与内部 rb_equal_opt/rb_equal 函数匹配时会中断。除非您为重复的成员资格检查创建一个集合,否则它不会变得更有效率。

VALUE
rb_ary_includes(VALUE ary, VALUE item)
{
  long i;
  VALUE e;

  for (i=0; i<RARRAY_LEN(ary); i++) {
    e = RARRAY_AREF(ary, i);
    switch (rb_equal_opt(e, item)) {
      case Qundef:
        if (rb_equal(e, item)) return Qtrue;
        break;
      case Qtrue:
        return Qtrue;
    }
  }
  return Qfalse;
}

member? 未在 Array 类中重新定义,而是使用 Enumerable 模块中未优化的实现,该实现逐字枚举所有元素:

static VALUE
member_i(RB_BLOCK_CALL_FUNC_ARGLIST(iter, args))
{
  struct MEMO *memo = MEMO_CAST(args);

  if (rb_equal(rb_enum_values_pack(argc, argv), memo->v1)) {
    MEMO_V2_SET(memo, Qtrue);
    rb_iter_break();
  }
  return Qnil;
}

static VALUE
enum_member(VALUE obj, VALUE val)
{
  struct MEMO *memo = MEMO_NEW(val, Qfalse, 0);

  rb_block_call(obj, id_each, 0, 0, member_i, (VALUE)memo);
  return memo->v2;
}

转换为 Ruby 代码,它执行以下操作:

def member?(value)
  memo = [value, false, 0]
  each_with_object(memo) do |each, memo|
    if each == memo[0]
      memo[1] = true 
      break
    end
  memo[1]
end

include?member? 都具有 O(n) 时间复杂度,因为它们都在数组中搜索第一次出现的期望值。

我们可以使用 Set 来获得 O(1) 访问时间,代价是必须首先创建数组的哈希表示。如果您反复检查同一阵列上的成员资格,则此初始投资可以很快得到回报。 Set 不是在 C 中实现的,而是作为普通的 Ruby 类实现的,底层 @hash 的 O(1) 访问时间仍然值得这样做。

下面是 Set 类的实现:

module Enumerable
  def to_set(klass = Set, *args, &block)
    klass.new(self, *args, &block)
  end
end

class Set
  def initialize(enum = nil, &block) # :yields: o
    @hash ||= Hash.new
    enum.nil? and return
    if block
      do_with_enum(enum) { |o| add(block[o]) }
    else
      merge(enum)
    end
  end

  def merge(enum)
    if enum.instance_of?(self.class)
      @hash.update(enum.instance_variable_get(:@hash))
    else
      do_with_enum(enum) { |o| add(o) }
    end
    self
  end

  def add(o)
    @hash[o] = true
    self
  end

  def include?(o)
    @hash.include?(o)
  end
  alias member? include?

  ...
end

如您所见,Set 类只是创建了一个内部 @hash 实例,将所有对象映射到 true,然后使用 Hash#include? 检查成员资格,这在 Hash 类中使用 O(1) 访问时间实现。

我不会讨论其他七种方法,因为它们的效率都较低。

实际上,除了上面列出的 11 种方法之外,还有更多具有 O(n) 复杂度的方法,但我决定不列出它们,因为它们扫描整个数组而不是在第一次匹配时中断。

不要使用这些:

# bad examples
array.grep(element).any? 
array.select { |each| each == element }.size > 0
...

说 Ruby 有 11 种方法可以做任何事情是多么的厚颜无耻!只要你说有人会指出你错过了#12,然后是#13,等等。为了说明我的观点,我将建议其他方式,但首先让我质疑您列举的 11 方式。首先,您很难将 indexfind_index(或 finddetect)算作单独的方法,因为它们只是同一方法的不同名称。其次,所有以 > 0 结尾的表达式都是不正确的,我敢肯定这是一个疏忽。 (续)
...arr.index(e) 例如,如果 arr[0] == e 则返回 0。如果 e 不存在,您会记得 arr.index(e) 返回 nil。但是,如果在 arr 中搜索 nil,则不能使用 index。 (与 rindex 相同的问题,未列出。)。将数组转换为集合然后使用集合方法有点牵强。为什么不转换为散列(使用数组中的键和任意值),然后使用散列方法?即使转换为集合是可以的,也可以使用其他集合方法,例如 !arr.to_set.add?(e)。 (续)
...正如所承诺的,这里还有一些可以使用的方法:arr.count(e) > 0arr != arr.dup.delete(e) arr != arr - [e]arr & [e] == [e]。也可以使用 selectreject
如果条件是不允许重复,并且知道列表中是否存在特定元素,我强烈建议使用 Set。设置的速度要快得多。当需要搜索时,真的不应该使用数组;它们最好用作队列,暂时存储事物以按顺序处理。 Hash 和 Set 更适合查看是否存在。
some_array.exclude?('some_string') 也很有用。
B
Boris Stitnicky

有几个答案建议使用 Array#include?,但有一个重要的警告:查看源代码,即使 Array#include? 确实执行循环:

rb_ary_includes(VALUE ary, VALUE item)
{
    long i;

    for (i=0; i<RARRAY_LEN(ary); i++) {
        if (rb_equal(RARRAY_AREF(ary, i), item)) {
            return Qtrue;
        }
    }
    return Qfalse;
}

在不循环的情况下测试单词存在的方法是为您的数组构造一个 trie。那里有许多 trie 实现(谷歌“ruby trie”)。我将在此示例中使用 rambling-trie

a = %w/cat dog bird/

require 'rambling-trie' # if necessary, gem install rambling-trie
trie = Rambling::Trie.create { |trie| a.each do |e| trie << e end }

现在我们已经准备好在 O(log n) 时间内测试数组中各种单词的存在,而无需循环它,使用与 Array#include? 相同的句法简单性,使用次线性 Trie#include?

trie.include? 'bird' #=> true
trie.include? 'duck' #=> false

a.each do ... end 嗯...不确定这不是循环
该循环只执行一次,在构造 trie 时。它是一个常数项,不会影响算法的复杂性。坦率地说,该算法必须至少循环一次数组才能知道那里有什么单词。一旦构建了 trie,就可以多次使用它来检查是否存在具有大致对数复杂度的单词。
请注意,这实际上包含一个循环;任何不是 O(1) 的东西都包含某种循环。它恰好是输入字符串字符的循环。另请注意,对于关心效率的人来说,除了已经提到的答案Set#include?;再加上使用符号而不是字符串,它可以是 O(1) 平均情况(如果你使用字符串,那么只计算散列是 O(n),其中 n 是字符串的长度)。或者,如果您想使用第三方库,您可以使用 O(1) 最坏情况的完美哈希。
AFAIK,Set 使用散列来索引其成员,因此实际上 Set#include? 应该对于分布良好的 Set 复杂度 O(1)(更具体地说是 O(input-size)散列和 O(log(n/bucket-number)) 用于搜索)
创建和维护 trie 的成本同样高。如果您在数组上执行许多搜索操作,那么填充和维护它的内存和时间成本是值得的,但是对于单个甚至数百或数千个检查,O(n) 非常适合。另一个不需要添加依赖项的选项是对数组进行排序或按排序顺序维护它,在这种情况下,可以使用二进制搜索 O(lg n) 操作来检查包含。
t
the Tin Man

如果您不想循环,则无法使用数组来执行此操作。您应该改用 Set 。

require 'set'
s = Set.new
100.times{|i| s << "foo#{i}"}
s.include?("foo99")
 => true
[1,2,3,4,5,6,7,8].to_set.include?(4) 
  => true

集合在内部像哈希一样工作,因此 Ruby 不需要遍历集合来查找项目,因为顾名思义,它生成键的哈希并创建内存映射,以便每个哈希指向内存中的某个点。上一个使用 Hash 完成的示例:

fake_array = {}
100.times{|i| fake_array["foo#{i}"] = 1}
fake_array.has_key?("foo99")
  => true

缺点是 Sets 和 Hash 键只能包含唯一项,如果您添加很多项,Ruby 将不得不在一定数量的项之后重新散列整个事物以构建适合更大键空间的新映射。有关这方面的更多信息,我建议您观看“MountainWest RubyConf 2014 - Big O in a Homemade Hash by Nathan Long”。

这是一个基准:

require 'benchmark'
require 'set'

array = []
set   = Set.new

10_000.times do |i|
  array << "foo#{i}"
  set   << "foo#{i}"
end

Benchmark.bm do |x|
  x.report("array") { 10_000.times { array.include?("foo9999") } }
  x.report("set  ") { 10_000.times { set.include?("foo9999")   } }
end

结果:

      user     system      total        real
array  7.020000   0.000000   7.020000 (  7.031525)
set    0.010000   0.000000   0.010000 (  0.004816)

如果您使用检测,那么您至少可以减少循环。检测将在第一个“检测到”项目处停止(为该项目传递的块评估为真)。此外,您可以告诉检测如果没有检测到该做什么(您可以传入一个 lambda)。
@aenw include? 不会在第一次命中时停止?
你是绝对正确的。我已经习惯了使用检测,以至于我忘记了包含。感谢您的评论 - 它确保我更新了我的知识。
include? 在第一次命中时停止,但如果该命中在列表末尾....任何依赖数组进行存储的解决方案都会随着列表的增长而降低性能,尤其是当必须在列表的末尾。 Hash 和 Set 没有这个问题,有序列表和二进制搜索也没有。
这几乎就是这个答案最初的内容:)
t
the Tin Man

这是执行此操作的另一种方法:使用 Array#index 方法。

它返回数组中第一次出现的元素的索引。

例如:

a = ['cat','dog','horse']
if a.index('dog')
    puts "dog exists in the array"
end

index() 也可以占用一个块:

例如:

a = ['cat','dog','horse']
puts a.index {|x| x.match /o/}

这将返回包含字母 'o' 的数组中第一个单词的索引。


index 仍然遍历数组,它只是返回元素的值。
a
akuhn

有趣的事实,

您可以使用 * 检查 case 表达式中的数组成员资格。

case element
when *array 
  ...
else
  ...
end

注意 when 子句中的小 *,它检查数组中的成员。

splat 运算符的所有常见魔术行为都适用,例如,如果 array 实际上不是数组而是单个元素,它将匹配该元素。


很高兴知道..!
在 case 语句中,它也是一个缓慢的第一次检查,所以我会在最后一个 when 中使用它,这样其他更快的检查就会很快被淘汰。
m
marcusshep

有多种方法可以实现这一点。其中一些如下:

a = [1,2,3,4,5]

2.in? a  #=> true

8.in? a #=> false

a.member? 1 #=> true

a.member? 8 #=> false

请注意,Object#in? 仅添加到 Rails(即 ActiveSupport)v3.1+。它在核心 Ruby 中不可用。
c
calvin

检查存在

使用include?

例子:

arr = [1, 2, 3]
arr.include?(1) -> true
arr.include?(4) -> false

支票不存在

使用exclude?

例子:

arr = %w(vietnam china japan)
arr.exclude?('usa') -> true
arr.exclude?('china') -> false

*.include?("some-string") 也适用于数组项的 exact 字符串匹配。
u
user3245240

这不仅会告诉您它存在,还会告诉您它出现了多少次:

 a = ['Cat', 'Dog', 'Bird']
 a.count("Dog")
 #=> 1

除非您想知道它出现了多少次,否则使用它是没有意义的,因为 .any? 将在找到第一个匹配元素后立即返回,.count 将始终处理整个数组。
虽然这在技术上可以判断是否存在某些东西,但如果您关心速度,这也不是正确的方法。
t
the Tin Man

你可以试试:

示例:如果数组中存在 Cat 和 Dog:

(['Cat','Dog','Bird'] & ['Cat','Dog'] ).size == 2   #or replace 2 with ['Cat','Dog].size

代替:

['Cat','Dog','Bird'].member?('Cat') and ['Cat','Dog','Bird'].include?('Dog')

注意:member?include? 相同。

这可以在一条线上完成工作!


O
OneCricketeer

如果您需要多次检查任何键,请将 arr 转换为 hash,然后检查 O(1)

arr = ['Cat', 'Dog', 'Bird']
hash = arr.map {|x| [x,true]}.to_h
 => {"Cat"=>true, "Dog"=>true, "Bird"=>true}
hash["Dog"]
 => true
hash["Insect"]
 => false

Hash#has_key?Array#include? 的性能

Parameter              Hash#has_key?                 Array#include 

Time Complexity         O(1) operation                O(n) operation 

Access Type             Accesses Hash[key] if it      Iterates through each element
                        returns any value then        of the array till it
                        true is returned to the       finds the value in Array
                        Hash#has_key? call
                        call

对于使用 include? 的单次检查很好


您是否仍在循环遍历数组以将其转换为哈希?
是的,我做到了,但现在我已经预先计算了答案来检查数组中是否存在任何元素。现在,下次当我搜索其他单词时,查询需要 O(1),尽管预计算需要 O(n)。其实我用过包括?在我的应用程序内部循环中检查数组中是否存在特定元素,它破坏了性能,它检查了 ruby-prof,这是瓶颈
....但是 O(n) 空间,而且,不是 O(n) 时间,因为正如@chris72205 正确指出的那样,您必须先遍历您的数组。 O(1) + O(n) = O(n)。所以事实上,这比包含更糟糕?
伙计,我只是说不要在循环内使用包含,对于使用包含的单次检查很好。所以请先阅读用例,如果您需要在循环内多次检查,效果会更好。
将数组转换为哈希是昂贵的,但使用数组进行搜索是错误的结构。数组更适合作为顺序可能很重要的队列;当包含/存在/唯一性很重要时,哈希和集合会更好。从正确的容器开始,问题没有实际意义。
d
davissp14

对于它的价值,Ruby docs 是解决这类问题的绝佳资源。

我还会记下您正在搜索的数组的长度。 include? 方法将运行复杂度为 O(n) 的线性搜索,根据数组的大小可能会变得非常丑陋。

如果您正在使用大型(已排序)数组,我会考虑编写一个 binary search algorithm,它应该不会太难并且最坏的情况是 O(log n)。

或者,如果您使用的是 Ruby 2.0,则可以利用 bsearch


二进制搜索假设数组是排序的(或以某种形式排序),这对于大型数组来说成本很高,通常会抵消优势。
二进制搜索还要求所有元素对都与 <=> 相当,但情况并非总是如此。例如,假设数组的元素是散列。
@CarySwoveland 应该或多或少暗示排序数组中的元素是可比较的。
x
xlembouras

如果我们不想使用 include?,这也可以:

['cat','dog','horse'].select{ |x| x == 'dog' }.any?

任何?也接受块:['cat','dog','horse'].any? { |x| x == '狗' }
s
shock_one

这条路怎么样?

['Cat', 'Dog', 'Bird'].index('Dog')

它仍然会遍历数组以找到元素。然后它必须返回该元素的索引。
R
Rahul Patel
['Cat', 'Dog', 'Bird'].detect { |x| x == 'Dog'}
=> "Dog"
!['Cat', 'Dog', 'Bird'].detect { |x| x == 'Dog'}.nil?
=> true

['Cat', nil, 'Dog'].detect { |x| x == nil } #=> nil。找到 nil 了吗?
J
Jon Schneider

如果您尝试在 MiniTest 单元测试中执行此操作,则可以使用 assert_includes。例子:

pets = ['Cat', 'Dog', 'Bird']
assert_includes(pets, 'Dog')      # -> passes
assert_includes(pets, 'Zebra')    # -> fails 

t
the Tin Man

还有另一种方法。

假设数组是 [ :edit, :update, :create, :show ],也许是整个 七种致命/宁静的罪

进一步玩弄从某个字符串中提取有效动作的想法:

"my brother would like me to update his profile"

然后:

[ :edit, :update, :create, :show ].select{|v| v if "my brother would like me to update his profile".downcase =~ /[,|.| |]#{v.to_s}[,|.| |]/}

您的正则表达式 /[,|.| |]#{v.to_s}[,|.| |]/ 让我觉得您想找到“被以下之一包围的动作名称:逗号、句号、空格或什么都没有”,但有一些细微的错误。 "|update|" 将返回 [:update],而 "update" 将返回 []。字符类 ([...]) 不使用管道 (|) 来分隔字符。即使我们将它们更改为组((...)),您也无法匹配空字符。所以你可能想要的正则表达式是 /(,|\.| |^)#{v.to_s}(,|\.| |$)/
正则表达式没问题(检查了 rubular.com) - 和@Rambatino:例如为什么会像 Amazon Echo ;)你可以说:“请把鸡肉添加到我的购物清单中”(而且 - 好吧 - 那么你必须将 :add 添加到数组中,但我想你明白了它的要点;)
“正则表达式没问题(检查 rubular.com)”。不行。您的正则表达式不会匹配字符串开头或结尾的关键字(例如“更新我兄弟的个人资料”)。如果您不想匹配开头或结尾,那么您的正则表达式仍然不行,因为关键字两侧的字符类应该是 /[,. ]/
正如@bkDJ 所说,正则表达式是错误的。 rubular.com/r/4EG04rANz6KET6
5
5 revs

我总是觉得运行一些基准来查看各种做事方式的相对速度很有趣。

在开头、中间或结尾查找数组元素会影响任何线性搜索,但几乎不会影响对 Set 的搜索。

将 Array 转换为 Set 会缩短处理时间,因此从 Array 中创建 Set 一次,或者从一开始就从 Set 开始。

这是基准代码:

# frozen_string_literal: true

require 'fruity'
require 'set'

ARRAY = (1..20_000).to_a
SET = ARRAY.to_set

DIVIDER = '-' * 20

def array_include?(elem)
  ARRAY.include?(elem)
end

def array_member?(elem)
  ARRAY.member?(elem)
end

def array_index(elem)
  ARRAY.index(elem) >= 0
end

def array_find_index(elem)
  ARRAY.find_index(elem) >= 0
end

def array_index_each(elem)
  ARRAY.index { |each| each == elem } >= 0
end

def array_find_index_each(elem)
  ARRAY.find_index { |each| each == elem } >= 0
end

def array_any_each(elem)
  ARRAY.any? { |each| each == elem }
end

def array_find_each(elem)
  ARRAY.find { |each| each == elem } != nil
end

def array_detect_each(elem)
  ARRAY.detect { |each| each == elem } != nil
end

def set_include?(elem)
  SET.include?(elem)
end

def set_member?(elem)
  SET.member?(elem)
end

puts format('Ruby v.%s', RUBY_VERSION)

{
  'First' => ARRAY.first,
  'Middle' => (ARRAY.size / 2).to_i,
  'Last' => ARRAY.last
}.each do |k, element|
  puts DIVIDER, k, DIVIDER

  compare do
    _array_include?        { array_include?(element)        }
    _array_member?         { array_member?(element)         }
    _array_index           { array_index(element)           }
    _array_find_index      { array_find_index(element)      }
    _array_index_each      { array_index_each(element)      }
    _array_find_index_each { array_find_index_each(element) }
    _array_any_each        { array_any_each(element)        }
    _array_find_each       { array_find_each(element)       }
    _array_detect_each     { array_detect_each(element)     }
  end
end

puts '', DIVIDER, 'Sets vs. Array.include?', DIVIDER
{
  'First' => ARRAY.first,
  'Middle' => (ARRAY.size / 2).to_i,
  'Last' => ARRAY.last
}.each do |k, element|
  puts DIVIDER, k, DIVIDER

  compare do
    _array_include? { array_include?(element) }
    _set_include?   { set_include?(element)   }
    _set_member?    { set_member?(element)    }
  end
end

在我的 Mac OS 笔记本电脑上运行时,会导致:

Ruby v.2.7.0
--------------------
First
--------------------
Running each test 65536 times. Test will take about 5 seconds.
_array_include? is similar to _array_index
_array_index is similar to _array_find_index
_array_find_index is faster than _array_any_each by 2x ± 1.0
_array_any_each is similar to _array_index_each
_array_index_each is similar to _array_find_index_each
_array_find_index_each is faster than _array_member? by 4x ± 1.0
_array_member? is faster than _array_detect_each by 2x ± 1.0
_array_detect_each is similar to _array_find_each
--------------------
Middle
--------------------
Running each test 32 times. Test will take about 2 seconds.
_array_include? is similar to _array_find_index
_array_find_index is similar to _array_index
_array_index is faster than _array_member? by 2x ± 0.1
_array_member? is faster than _array_index_each by 2x ± 0.1
_array_index_each is similar to _array_find_index_each
_array_find_index_each is similar to _array_any_each
_array_any_each is faster than _array_detect_each by 30.000000000000004% ± 10.0%
_array_detect_each is similar to _array_find_each
--------------------
Last
--------------------
Running each test 16 times. Test will take about 2 seconds.
_array_include? is faster than _array_find_index by 10.000000000000009% ± 10.0%
_array_find_index is similar to _array_index
_array_index is faster than _array_member? by 3x ± 0.1
_array_member? is faster than _array_find_index_each by 2x ± 0.1
_array_find_index_each is similar to _array_index_each
_array_index_each is similar to _array_any_each
_array_any_each is faster than _array_detect_each by 30.000000000000004% ± 10.0%
_array_detect_each is similar to _array_find_each

--------------------
Sets vs. Array.include?
--------------------
--------------------
First
--------------------
Running each test 65536 times. Test will take about 1 second.
_array_include? is similar to _set_include?
_set_include? is similar to _set_member?
--------------------
Middle
--------------------
Running each test 65536 times. Test will take about 2 minutes.
_set_member? is similar to _set_include?
_set_include? is faster than _array_include? by 1400x ± 1000.0
--------------------
Last
--------------------
Running each test 65536 times. Test will take about 4 minutes.
_set_member? is similar to _set_include?
_set_include? is faster than _array_include? by 3000x ± 1000.0

基本上,如果我要搜索包含,结果告诉我对所有内容都使用 Set ,除非我可以保证第一个元素是我想要的元素,这不太可能。将元素插入哈希时会有一些开销,但是搜索时间要快得多,我认为这不应该是一个考虑因素。同样,如果您需要搜索它,请不要使用 Array,请使用 Set。 (或哈希。)

Array 越小,Array 方法运行得越快,但它们仍然跟不上,尽管在小数组中差异可能很小。

“First”、“Middle”和“Last”反映了对正在搜索的元素的 ARRAY 使用 firstsize / 2last。搜索 ARRAYSET 变量时将使用该元素。

对与 > 0 进行比较的方法进行了少量更改,因为对于 index 类型测试,测试应该是 >= 0

有关 Fruity 及其方法的更多信息,请参阅其 README


g
gitb

如果要返回的值不仅仅是真或假,请使用

array.find{|x| x == 'Dog'}

如果它存在于列表中,这将返回 'Dog',否则返回 nil。


或者,如果您确实想要真/假(不是值),但也想与这样的块进行比较,请使用 array.any?{|x| x == 'Dog'}
t
the Tin Man

如果您不想使用 include? 您可以先将元素包装在一个数组中,然后检查被包装的元素是否等于数组和被包装元素的交集。这将返回一个基于相等的布尔值。

def in_array?(array, item)
    item = [item] unless item.is_a?(Array)
    item == array & item
end

W
Wand Maker

这是另一种方法:

arr = ['Cat', 'Dog', 'Bird']
e = 'Dog'

present = arr.size != (arr - [e]).size

这是一种非常低效的方法!我没有拒绝投票,因为它在技术上并不正确,有人可能会通过阅读它来了解 Ruby,但上面有很多更好的答案。
Conehead,您可以简化为 arr != arr - [e]arr & [e] == [e] 是同理的另一种方式。
@CarySwoveland 不要取笑巫师的帽子 ;-)
我指的是巫师的头,而不是帽子,我怀着最大的敬意。
A
Abhishek kumar

它有很多方法可以在任何数组中查找元素,但最简单的方法是“在?”方法。

example:
arr = [1,2,3,4]
number = 1
puts "yes #{number} is present in arr" if number.in? arr

注意:如 this answer 中所述,方法 in? 需要导入 ActiveSupportrequire active_support
如果您使用 core extensions,它不需要所有 ActiveSupport。
M
Matthew Maurice
array = [ 'Cat', 'Dog', 'Bird' ]
array.include?("Dog")