Check if a value exists in an array in Ruby
我有一个值
如何在不循环的情况下检查数组中是否存在它?是否有一种简单的方法来检查该值是否存在,而不是更多?
你在找
1 2 | >> ['Cat', 'Dog', 'Bird'].include? 'Dog' => true |
正如@campaterson指出的那样,自v3.1以来,EDOCX1(轨道的一部分)中有一个
1 | 'Unicorn'.in?(['Cat', 'Dog', 'Bird']) # => false |
Otoh,Ruby本身没有
如其他人所指出的,对于所有的
1 | ['Cat', 'Dog', 'Bird'].include?('Unicorn') # => false |
请注意,如果您的数组中有许多值,则将逐个检查这些值(即
1 2 3 4 5 6 7 8 9 | 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 |
快速测试显示,在10个元素
最后一个总结:在
尝试
1 | ['Cat', 'Dog', 'Bird'].include?('Dog') |
使用
1 2 3 | a = %w/Cat Dog Bird/ a.include? 'Dog' |
或者,如果完成了许多测试,1您可以去掉循环(甚至是
1 2 | h = Hash[[a, a].transpose] h['Dog'] |
1。我希望这是显而易见的,但为了避免反对意见:是的,对于几个查找,hash[]和transmose操作控制了配置文件,并且每个操作本身都是O(N)。
如果你想一个街区检查,你可以尝试任何?还是全部?.
1 2 3 | %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 |
详情如下:http://ruby-doc.org/core-1.9.3/enumerable.html我的灵感来自这里:https://stackoverflow.com/a/10342734/576497
Ruby有11种方法来查找数组中的元素。
首选是
或者对于重复访问,创建一个集合,然后调用
这些都是,
1 2 3 4 5 6 7 8 9 10 11 | 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 |
如果存在元素,所有这些元素都返回一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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代码,这是关于以下内容的
1 2 3 4 5 6 7 8 9 | 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 |
我们可以使用一个集合来获取
这是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 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 |
如您所见,
我不会讨论其他7种方法,因为它们都不那么有效。
实际上,除了上面列出的11个方法之外,还有更多的方法具有
别用这些,
1 2 3 4 | # bad examples array.grep(element).any? array.select { |each| each == element }.size > 0 ... |
有几个答案表明
1 2 3 4 5 6 7 8 9 10 11 | 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实现(google"ruby trie")。在这个例子中,我将使用
1 2 3 4 | 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 } |
现在,我们准备在不循环的情况下,在
1 2 | trie.include? 'bird' #=> true trie.include? 'duck' #=> false |
如果不想循环,就不能用数组来实现。你应该用一套来代替。
1 2 3 4 5 6 7 | 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不需要通过集合循环来查找项,因为顾名思义,它生成键的散列并创建内存映射,以便每个散列都指向内存中的某个点。上一个示例使用哈希完成:
1 2 3 4 | fake_array = {} 100.times{|i| fake_array["foo#{i}"] = 1} fake_array.has_key?("foo99") => true |
缺点是集合键和散列键只能包含唯一的项,如果您添加了很多项,Ruby将不得不在特定数量的项之后重新刷新整个项,以构建适合更大的键空间的新映射。关于这方面的更多信息,我建议您观看2014年的西山红宝石节——内森·朗自制土豆泥中的大O。
以下是一个基准:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 |
结果是:
1 2 3 | user system total real array 7.020000 0.000000 7.020000 ( 7.031525) set 0.010000 0.000000 0.010000 ( 0.004816) |
这是另一种方法:使用数组索引方法。
它返回数组中元素第一次出现的索引。
例子:
1 2 3 4 | a = ['cat','dog','horse'] if a.index('dog') puts"dog exists in the array" end |
index()也可以接受一个块
例如
1 2 | a = ['cat','dog','horse'] puts a.index {|x| x.match /o/} |
在这里,返回数组中包含字母"o"的第一个单词的索引。
有趣的事实,
可以使用
1 2 3 4 5 6 | case element when *array ... else ... end |
注意when子句中的小
splat运算符的所有常见魔术行为都适用,因此,例如,如果
实现这一点有多种方法。其中一些如下:
1 2 3 4 5 6 7 8 9 | a = [1,2,3,4,5] 2.in? a #=> true 8.in? a #=> false a.member? 1 #=> true a.member? 8 #=> false |
这不仅会告诉您它存在,还会告诉您它出现了多少次:
1 2 3 | a = ['Cat', 'Dog', 'Bird'] a.count("Dog") #=> 1 |
如果你有更多的价值观…你可以试试:
示例:如果数组中存在cat和dog:
1 | (['Cat','Dog','Bird'] & ['Cat','Dog'] ).size == 2 #or replace 2 with ['Cat','Dog].size |
而不是:
1 | ['Cat','Dog','Bird'].member?('Cat') and ['Cat','Dog','Bird'].include?('Dog') |
注意:会员?包括在内?都一样。
这可以一行完成工作!
不管它有什么价值,Ruby文档对于这些问题来说都是一个令人惊奇的资源。
我还会记下你搜索的数组的长度。
如果你使用的是一个大的(排序的)数组,我会考虑编写一个二进制搜索算法,它不应该太难,并且有一个最坏的情况O(log n)。
或者,如果您使用的是Ruby2.0,那么您可以利用
如果需要对任何键进行多次检查,请将
1 2 3 4 5 6 7 | 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的性能有u键吗?相对于阵列包括?
1 2 3 4 5 6 7 8 9 | 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 |
对于单次检查,使用
如果我们不想使用
1 | ['cat','dog','horse'].select{ |x| x == 'dog' }.any? |
1 2 3 4 | ['Cat', 'Dog', 'Bird'].detect { |x| x == 'Dog'} =>"Dog" !['Cat', 'Dog', 'Bird'].detect { |x| x == 'Dog'}.nil? => true |
还有另一条路!
假设数组是[:edit,:update,:create,:show]-那么可能是整个七个致命/宁静的罪恶:)
更进一步的玩弄从某根绳子上拉出一个有效动作的想法-比如说
my brother would like me to update his profile
解决方案
1 | [ :edit, :update, :create, :show ].select{|v| v if"my brother would like me to update his profile".downcase =~ /[,|.| |]#{v.to_s}[,|.| |]/} |
这边怎么样?
1 | ['Cat', 'Dog', 'Bird'].index('Dog') |
如果您试图在小型测试单元测试中进行此操作,则可以使用
1 2 3 | pets = ['Cat', 'Dog', 'Bird'] assert_includes(pets, 'Dog') # -> passes assert_includes(pets, 'Zebra') # -> fails |
如果要返回值而不仅仅是true或false,请使用
1 | array.find{|x| x == 'Dog'} |
如果列表中存在"dog",则返回"dog",否则返回nil。
还有一种方法可以做到这一点:
1 2 3 4 | arr = ['Cat', 'Dog', 'Bird'] e = 'Dog' present = arr.size != (arr - [e]).size |
1 2 | array = [ 'Cat', 'Dog', 'Bird' ] array.include?("Dog") |
如果不想使用include?可以先将元素包装在数组中,然后检查包装元素是否等于数组与包装元素的交集。这将返回基于相等的布尔值。
1 2 3 4 | def in_array?(array, item) item = [item] unless item.is_a?(Array) item == array & item end |
it has many ways to find a element in any array but the simplest way is 'in ?' method.
1 2 3 4 | example: arr = [1,2,3,4] number = 1 puts"yes #{number} is present in arr" if number.in? arr |