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


1313

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

如何检查它是否存在于数组中而不遍​​历它?有没有简单的方法检查值是否存在,仅此而已?


5
使用.include?方法。它返回一个布尔值,这就是您想要的。在您的情况下,只需键入:['Cat','Dog','Bird']。include('Dog'),它应该返回布尔值true。
Jwan622'2

不使用包括? 如果要检查多次以检查是否存在包含数组中的不同值,请使用这种方法。每次将使用O(n)操作遍历数组进行每次搜索,而是进行哈希处理hash = arr.map {|x| [x,true]}.to_h,现在检查是否hash.has_key? 'Dog' 返回true
aqfaridi

3
您不能完全“不循环遍历”。从逻辑上讲这是不可能的,计算机只是无法确定数组是否包含该元素,而无需遍历这些元素来检查其中是否有任何元素正在寻找。当然,除非它是空的。然后,我猜您不需要循环。
Tim M.

有关在数组和集合中查找元素的各种方法的差异的测试,请参见下面的基准。stackoverflow.com/a/60404934/128421
锡人

Answers:


1943

您正在寻找include?

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

73
备用语法:%w(Cat Dog Bird).include? 'Dog'
scarver2

184
有时我希望它不是“包含”。我总是把它和includes混在一起。
亨利·邱

19
让我只注意到在内部,#include?仍然会执行循环。不过,可以通过显式编写循环来保存编码器。我添加了一个答案,该答案可以真正执行任务而无需循环。
鲍里斯·史提尼克

8
@HenleyChiu我被称为[ 'Dog', 'Bird', 'Cat' ].has? 'Dog'

4
@AlfonsoVergara是的,数组的任何解决方案都必须在内部进行某种循环。没有循环就无法测试数组的成员资格。如果您甚至不想在内部进行任何循环,就需要使用其他数据结构,例如具有固定大小的键的完美哈希表。鉴于没有内部循环就无法测试数组中的成员资格的问题,我将问题解释为“无需自己明确编写循环”
Brian Campbell

249

有一种in?方法ActiveSupport(滑轨的一部分),因为V3.1,如通过@campaterson指出。因此,在Rails中,或者,如果您可以require 'active_support',您可以编写:

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

OTOH,Ruby本身没有in运算符或#in?方法,即使以前已经提出过,特别是红宝石核心的顶尖成员远藤裕介也曾提出过。

如其他人指出的那样,反向方法include?存在,所有Enumerable小号包括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

一个快速测试表明,调用include?一个10元的Set约3.5倍比调用它的等效快Array(如果未找到该元素)。

最后的闭注:使用时要小心include?Range,也有细微之处,所以要参考的文档,并与比较cover?...


2
尽管Ruby #in?的核心不包含Ruby ,但是如果您使用的是Rails,则可以使用它。api.rubyonrails.org/classes/Object.html#method-i-in-3F(我知道这是Ruby,而不是Rails问题,但它可能会帮助希望#in?在Rails中使用的任何人。看起来它是在Rails中添加的3.1 apidock.com/rails/Object/in%3F
campeterson

166

尝试

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

这是较旧的语法,请看^^^
@brian

62
@jahrichie您究竟在此答案中将“旧语法”视为什么,可选括号?
丹尼斯2014年

3
我同意@Dennis,它不老,括号是可选的,并且在大多数情况下是一种很好的实践....例如,如果在句子中尝试在一行中使用不带括号的包含,我的意思是,这取决于您的情况,应该还是必须使用方括号(根本与“旧”红宝石语法
无关

这是我在三元运算中可以使用的唯一语法。
迈克尔·卡梅隆

49

用途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)


48

如果要按块检查,可以尝试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

我的灵感来自“ 评估数组中是否有红宝石


1
如果您要检查任何/所有这些字符串是否包含在另一个字符串/常量中,则非常有用
thanikkal 2012年

43

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

首选的是,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

true如果该元素存在,它们都将返回ish值。

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

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)访问时间,但必须先创建数组的Hash表示。如果您反复检查同一阵列的成员资格,则此初始投资可以很快得到回报。Set不是用C实现的,但作为普通的Ruby类,底层的O(1)访问时间仍然@hash使它值得。

这是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),例如,返回0如果arr[0] == e。如果不存在,您将召回arr.index(e)退货。但是,如果要在中搜索,则无法使用。(与相同的问题,未列出。)。将数组转换为集合,然后采用集合方法有点麻烦。为什么不转换为哈希(使用数组中的键和任意值),然后使用哈希方法呢?即使可以转换为集合,也可以使用其他集合方法,例如。(续)nileindexnilarrrindex!arr.to_set.add?(e)
卡里·斯沃夫兰

......正如所承诺的,在这里是有可能被使用的几个方法:arr.count(e) > 0arr != arr.dup.delete(e) arr != arr - [e]arr & [e] == [e]。一个人也可以雇用selectreject
卡里·斯沃夫兰

如果条件是不允许重复,并且知道列表中是否存在特定元素,我强烈建议您使用Set。设置速度更快。当需要搜索时,实际上不应该使用数组。最好将它们用作队列,在此处将事物临时存储以按顺序进行处理。哈希和集合更适合查看是否存在。
锡人

30

有几个答案提示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 }

现在,我们准备使用sublinear O(log n),以与相同的语法简单性Array#include?,测试数组中各个单词的存在,而无需在时间上进行遍历Trie#include?

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

8
a.each do ... end嗯...不确定那不是一个循环
Doorknob 2013年

27
注意,这实际上包括一个循环。任何不是O(1)的东西都包含某种循环。它只是碰巧在输入字符串的字符上循环。还需要注意的是,Set#include?对于那些关心效率的人,已经提到了一个答案。再加上使用符号而不是字符串,则可能是O(1)的平均情况(如果使用字符串,则仅计算哈希为O(n),其中n是字符串的长度)。或者,如果您想使用第三方库,则可以使用完美的哈希,这是O(1)最坏的情况。
布莱恩·坎贝尔

4
AFAIK Set使用散列来为其成员建立索引,因此,对于分布良好的散列来说,实际上Set#include? 应该具有O(1)的复杂度Set(更具体而言,对于散列而言,它的复杂度为O(input-size),对于散列而言,其复杂度为O(log(n / bucket-number)))搜索)
Uri Agassi 2014年

11
创建和维护该Trie的成本也是如此。如果您要在数组上执行许多搜索操作,那么填充一个trie并维护它的内存和时间成本是值得的,但是对于单个甚至数百或数千个检查,O(n)都是非常合适的。不需要添加依赖项的另一个选项是对数组进行排序或以排序顺序对其进行维护,在这种情况下,可以使用二进制搜索O(lg n)操作来检查包含性。
口语代码

1
@speakingcode,从实用的角度来看,您可能是正确的。但是OP要求“检查值是否存在,仅此而已,没有循环”。当我写下这个答案时,这里有许多实用的解决方案,但实际上并不能满足要求者的字面要求。您对BST与尝试有关的观察是正确的,但是对于字符串来说,trie是完成任务的正确工具,即使Wikipedia也知道这一点。构建和维护良好实施的树皮的复杂性令人惊讶。
Boris Stitnicky 2014年

18

如果您不想循环,则无法使用Arrays进行循环。您应该改用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

缺点是Set和Hash键只能包含唯一项,如果添加很多项,Ruby将必须在一定数量的项后重新散列整个内容,以构建适合较大键空间的新映射。有关此的更多信息,我建议您观看“ MountainWest RubyConf 2014-Nathan Long的自制哈希中的Big O ”。

这是一个基准:

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)

如果使用检测,则至少可以减少循环。detect将在“检测到”的第一个项目处停止(为该项目传递的块评估为true)。此外,您可以告诉检测到什么也没检测到该怎么办(您可以传入lambda)。
aenw

1
@aenw不会include?一击就停吗?
Kimmo Lehto 2014年

你是绝对正确的。我很习惯使用检测,而我忘记了包含。感谢您的评论-这可以确保我更新知识。
2014年

include?停在第一个匹配项上,但是如果该匹配项在列表的末尾。...依赖数组存储的任何解决方案都会随着列表的增长而降低性能,尤其是当必须在列表的末尾查找元素时清单。哈希和Set没问题,有序列表和二进制搜索也没有。
锡人

这基本上就是这个答案的
初衷

17

这是另一种方法:使用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仍然在数组上迭代,它只返回元素的值。
Tin Man

13

有趣的事实,

您可以*用来检查case表达式中的数组成员身份。

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

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

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


很高兴知道..!
卡里·斯沃夫兰

在case语句中进行第一次检查也很慢,因此我将在最后一次使用它,when以便更快地进行其他检查以快速淘汰。
铁皮人

9

有多种方法可以实现此目的。其中一些如下:

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

2.in? a  #=> true

8.in? a #=> false

a.member? 1 #=> true

a.member? 8 #=> false

6
请注意,Object#in?它仅添加到Rails(即ActiveSupport)v3.1 +中。在核心Ruby中不可用。
汤姆·罗德

5

如果您需要多次检查任何键,请转换arrhash,然后签入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?数组#包含

时间复杂度O(1)运算O(n)运算 

访问类型如果对每个元素进行迭代,则访问Hash [key]
                        返回数组的任何值,直到它
                        返回true以在数组中查找值
                        Hash#has_key?呼叫
                        呼叫    

对于单次检查使用include?就可以了


您是否仍在遍历数组以将其转换为哈希?
chrisjacob

2
是的,我做到了,但是现在我已经预先计算了答案,以检查数组中是否存在任何元素。现在,下次我搜索其他单词时,将使用O(1)进行查询,尽管预计算将使用O(n)。其实我用过包括吗?在我的应用程序内部循环中,用于检查数组中是否存在特定元素,这会破坏性能,它在ruby-prof中进行了检查,这是瓶颈
aqfaridi

1
....但是是O(n)空间,也不是O(n)时间,因为正如@ chris72205正确指出的那样,您必须首先遍历数组。O(1)+ O(n)= O(n)。因此,实际上,这比包含还差吗?
Rambatino

杜德我只是说不要在循环内使用include,对于一次使用include的检查是很好的。因此,请先阅读用例,如果您需要多次检查循环,则更好。
aqfaridi

将数组转换为哈希值很昂贵,但是使用数组进行搜索是错误的结构。数组最好作为顺序可能很重要的队列。当包含/存在/唯一性很重要时,哈希和集合会更好。从正确的容器开始,然后问题就解决了。
锡人

4

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

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

12
除非您想知道它出现了多少次,否则没有意义,尽管它.any?会在找到第一个匹配元素后立即返回,.count它将始终处理整个数组。
Zaz 2014年

尽管这在技术上会告诉的东西是否存在,它也不要,如果你关心速度做正确的方式。
锡人

4

对于它的价值,Ruby文档对于这类问题而言是一个了不起的资源。

我还要记下要搜索的数组的长度。该include?方法将运行具有O(n)复杂度的线性搜索,根据数组的大小,该搜索可能很难看。

如果您使用的是大型(排序的)数组,我会考虑编写一种二进制搜索算法,该算法应该不太困难,并且具有O(log n)的最坏情况。

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


3
二进制搜索假定该数组已排序(或以某种形式排序),这对于大型数组可能会非常昂贵,通常会失去优势。
Tin Man

二进制搜索还要求所有成对的元素都必须与进行比较<=>,但情况并非总是如此。例如,假设数组的元素是哈希。
卡里·斯沃夫兰

1
@CarySwoveland应该或多或少暗示排序数组中的元素是可比较的。
davissp14

4

你可以试试:

示例:如果数组中存在猫和狗:

(['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?相同。

这样可以一站式完成工作!


3

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

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

3
任何?还接受以下代码:['cat','dog','horse']。any?{| x | x =='dog'}
maikonas


2

这样呢

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

它仍然要遍历数组以查找元素。然后,它必须返回该元素的索引。
Tin Man

2

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

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

2

还有另一种解决方法。

假设阵列[ :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}[,|.| |]/}

1
您的正则表达式/[,|.| |]#{v.to_s}[,|.| |]/使我觉得您想找到“动作的名称,并用以下各项之一包围:逗号,句点,空格或什么都不做”,但其中存在一些细微的错误。"|update|"将返回[:update]"update"将返回[]。字符类([...])不使用竖线(|)分隔字符。即使我们将它们更改为组((...)),也无法匹配空字符。因此,您可能想要的正则表达式是/(,|\.| |^)#{v.to_s}(,|\.| |$)/
bkDJ

正则表达式还可以(请检查rubular.com)-@Rambatino:例如为什么要使用Amazon Echo;)您可以说:“请将鸡肉添加到我的购物清单中”(而且-那么-那么您必须将:add添加到数组中,但我想您已经明白了它的要点;)
walt_die

1
“正则表达式还可以(选中rubular.com)”。不好 您的正则表达式与字符串开头或结尾处的关键字不匹配(例如“更新我的兄弟的个人资料”)。如果您不想匹配开始或结尾,则您的正则表达式仍然无法正常运行,因为关键字两侧的字符类应为/[,. ]/
bkDJ

正如@bkDJ所说,正则表达式是错误的。rubular.com/r/4EG04rANz6KET6
锡人

1

如果要返回的值不只是true或false,请使用

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

如果列表中存在“ Dog”,则返回“ Dog”,否则返回nil。


或使用array.any?{|x| x == 'Dog'},如果你想真/假(不是值),同时也希望以比较像这样的块。
mahemoff

0

这是执行此操作的另一种方法:

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

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

1
这是一种极其低效的方法!我并不是在投票,因为它在技术上不是错误的,有人可能会从阅读中学习到有关Ruby的知识,但是上面有很多更好的答案。
jibberia

锥头,您可以简化为arr != arr - [e]arr & [e] == [e]是沿着相同路线的另一种方式。
卡里·斯沃夫兰

@CarySwoveland不要取笑巫师的帽子;-)
魔杖制造者

我最敬重的是指巫师的头,而不是帽子。
卡里·斯沃夫兰



0

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

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

-1

我总是发现运行一些基准测试来查看各种操作方式的相对速度很有趣。

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

将数组转换为Set将造成处理时间的损失,因此,请从数组创建一次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,除非我可以保证第一个元素是我想要的元素,这不太可能。将元素插入到哈希中时会产生一些开销,但是搜索时间是如此之快,我不认为这是考虑因素。同样,如果您需要搜索它,请不要使用数组,而要使用Set。(或哈希)。

Array越小,Array方法运行的速度就越快,但是它们仍然不会跟上,尽管在较小的数组中差异可能很小。

“第一”,“中”和“最后”反映使用的firstsize / 2last用于ARRAY为元素正在搜索。搜索ARRAYSET变量时将使用该元素。

> 0由于要>= 0index类型进行测试,因此对要比较的方法进行了较小的更改。

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

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.