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

Posted

技术标签:

【中文标题】如何检查一个值是不是存在于Ruby中的数组中【英文标题】:How to check if a value exists in an array in Ruby如何检查一个值是否存在于Ruby中的数组中 【发布时间】:2010-12-31 11:29:36 【问题描述】:

我有一个值'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' returns 是否为真 你不能真正做到完全“不循环”。这在逻辑上是不可能的,计算机无法确定数组是否包含该元素,而无需遍历元素以检查它们是否是它正在搜索的元素。当然,除非它是空的。那我猜你不需要循环了。 请参阅下面的基准测试,以测试在 Array 和 Set 中查找元素的各种方法的差异。 ***.com/a/60404934/128421 【参考方案1】:

你正在寻找include?:

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

【讨论】:

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

使用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) 本身。

【讨论】:

【参考方案3】:

试试

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

【讨论】:

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

@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 的***成员。

正如其他人指出的那样,存在反向方法include?,适用于所有Enumerables,包括ArrayHashSetRange

['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 感谢您,特别感谢您对以下事实的评论:即使循环可能隐藏起来,提问者的原始点也无法确定数组中是否存在“不循环遍历它”是不可能的在像Array#include? 这样的简单方法调用背后——以及如何实际上避免这种情况(使用Set),这是否很重要。这个答案得到了我的支持,尽管我可能在最后留下了关于in? 的内容。【参考方案5】:

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

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

更多信息请参见Enumerable。

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

【讨论】:

如果您想检查任何/所有这些字符串是否包含在另一个字符串/常量中,这非常有用【参考方案6】:

还有其他方法。

假设数组是[ :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【参考方案7】:

几个答案建议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,就可以多次使用它来检查是否存在具有大致对数复杂度的单词。 请注意,这实际上包含一个循环;任何不是 O(1) 的东西都包含某种循环。它恰好是输入字符串字符的循环。另请注意,对于关心效率的人,已经提到了Set#include? 的答案;再加上使用符号而不是字符串,它可以是 O(1) 平均情况(如果你使用字符串,那么只计算哈希是 O(​​n),其中 n 是字符串的长度)。或者,如果您想使用第三方库,您可以使用 O(1) 最坏情况的完美哈希。 AFAIK, Set 使用散列来索引其成员,所以实际上Set#include? 应该对于分布良好的Set(更多特别是 O(input-size) 用于散列,O(log(n/bucket-number)) 用于搜索) 创建和维护 trie 的成本同样高。如果您在数组上执行许多搜索操作,那么填充和维护它的内存和时间成本是值得的,但对于单个甚至数百或数千个检查,O(n) 非常适合。另一个不需要添加依赖项的选项是对数组进行排序或按排序顺序维护它,在这种情况下,可以使用二进制搜索 O(lg n) 操作来检查包含。【参考方案8】:

不管怎样,Ruby docs 是解决这类问题的绝佳资源。

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

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

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

【讨论】:

二分查找假定数组已排序(或以某种形式排序),这对于大型数组而言代价高昂,通常会抵消优势。 二分搜索还要求所有元素对都与&lt;=&gt; 相当,但情况并非总是如此。例如,假设数组的元素是散列。 @CarySwoveland 应该或多或少暗示排序数组中的元素是可比较的。【参考方案9】:

这是另一种方法:使用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 仍然遍历数组,它只是返回元素的值。【参考方案10】:

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

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

【讨论】:

任何?也接受块:['cat','dog','horse'].any? |x| x == '狗' 【参考方案11】:

你可以试试:

示例:如果数组中存在 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? 是一样的。

这样一条线就能搞定!

【讨论】:

【参考方案12】:

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

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

【讨论】:

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

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

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

【讨论】:

【参考方案14】:

如果您不想循环,则无法使用数组来执行此操作。您应该改用 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 不需要遍历集合来查找项目,因为顾名思义,它会生成键的哈希并创建内存映射,以便每个哈希指向某个点在记忆中。上一个使用哈希完成的示例:

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 没有这个问题,有序列表和二分搜索也没有。 这几乎就是这个答案的初衷:)【参考方案15】:

这样怎么样?

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

【讨论】:

它仍然会遍历数组以找到元素。然后它必须返回那个元素的索引。【参考方案16】:

这里还有另一种方法:

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

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

【讨论】:

这是一种非常低效的方法!我没有反对,因为它在技术上并不正确,有人可能会从阅读中学到一些关于 Ruby 的知识,但上面有很多更好的答案。 Conehead,你可以简化为arr != arr - [e]arr &amp; [e] == [e] 是另一种方式。 @CarySwoveland 不要取笑巫师的帽子 ;-) 我指的是巫师的头,而不是帽子,非常尊重。【参考方案17】:

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

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 中不可用。【参考方案18】:
['Cat', 'Dog', 'Bird'].detect  |x| x == 'Dog'
=> "Dog"
!['Cat', 'Dog', 'Bird'].detect  |x| x == 'Dog'.nil?
=> true

【讨论】:

['Cat', nil, 'Dog'].detect |x| x == nil #=&gt; nil。找到nil 了吗?【参考方案19】:
array = [ 'Cat', 'Dog', 'Bird' ]
array.include?("Dog")

【讨论】:

【参考方案20】:

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)算作单独的方法,因为它们只是同一方法的不同名称。其次,所有以&gt; 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) &gt; 0arr != arr.dup.delete(e) arr != arr - [e]arr &amp; [e] == [e]。也可以使用selectreject 我强烈建议使用Set,如果标准是不允许重复,并且知道列表中是否存在特定元素。设置的速度要快得多。当需要搜索时,真的不应该使用数组;它们最好用作队列,暂时存储事物以按顺序处理。 Hash 和 Set 更适合查看是否存在。 some_array.exclude?('some_string') 也很有用。【参考方案21】:

有趣的事实,

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

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

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

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

【讨论】:

很高兴知道..! 在 case 语句中的第一次检查也会很慢,所以我会在最后一个when 使用它,这样其他更快的检查就会很快被淘汰。【参考方案22】:

如果您需要多次检查任何键,请将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? 的性能对比

参数 Hash#has_key?数组#include 时间复杂度 O(1) 操作 O(n) 操作 访问类型访问 Hash[key] 如果它遍历每个元素 然后返回数组的任何值,直到它 返回 true 以查找 Array 中的值 哈希#has_key?称呼 称呼

使用include? 进行单次检查就可以了

【讨论】:

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

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

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。【参考方案24】:

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

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

【讨论】:

【参考方案25】:

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

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

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

【讨论】:

如果您确实想要真/假(不是值),但也想与这样的块进行比较,请使用array.any?|x| x == 'Dog'【参考方案26】:

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

在开头、中间或结尾处查找数组元素会影响任何线性搜索,但几乎不会影响对 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”反映了使用 firstsize / 2last 表示正在搜索的元素的 ARRAY。搜索 ARRAYSET 变量时将使用该元素。

对与&gt; 0 进行比较的方法进行了细微更改,因为对于index 类型测试,测试应该是&gt;= 0

有关 Fruity 及其方法的更多信息,请访问其README。

【讨论】:

【参考方案27】:

检查存在

使用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 字符串匹配。

以上是关于如何检查一个值是不是存在于Ruby中的数组中的主要内容,如果未能解决你的问题,请参考以下文章

Hive:如何检查一个数组的值是不是存在于另一个数组中?

Flutter & Firebase:如何检查某个项目是不是存在于 Firebase 文档中的数组中

如何检查一个元素是不是在数组中

检查数组中的所有值是不是都存在于数据库列中

如何根据 JavaScript 中的值检查对象是不是在数组中?

检查多维数组的任何子数组中的特定键是不是存在特定值