Rubyの配列に値が存在するかどうかを確認する方法


1315

'Dog'と配列があり['Cat', 'Dog', 'Bird']ます。

ループせずに配列に存在するかどうかを確認するにはどうすればよいですか?値が存在するかどうかを確認する簡単な方法はありますか?


5
.includeを使用しますか?方法。必要なブール値を返します。あなたのケースでは、単に入力してください:['Cat'、 'Dog'、 'Bird']。include( 'Dog')and it should return the boolean true。
Jwan622 2016

インクルードを使用しないでください? 異なる値が配列内に存在するかどうかを複数回チェックする場合は、メソッドを含めるかどうか 毎回配列を反復処理してO(n)操作を実行し、毎回検索します。代わりにハッシュを作成し、trueが返されるhash = arr.map {|x| [x,true]}.to_hかどうかを確認しhash.has_key? 'Dog' ます
aqfaridi

3
完全に「ループすることなしに」それを完全に行うことはできません。論理的に不可能です。コンピュータは、要素がループして要素が含まれているかどうかを確認できず、要素が検索されていないかどうかを確認できません。もちろん、それが空でない限り。その後、私はあなたがループを必要としないと思います。
Tim M.

配列とセットの要素を検索するさまざまな方法の違いのテストについては、以下のベンチマークを参照してください。stackoverflow.com/a/60404934/128421
ティンマン

回答:


1946

あなたが探していますinclude?

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

73
代替構文:%w(Cat Dog Bird).include? 'Dog'
scarver2

184
時々私はそれが含まれない「含む」ことを望みます。常にインクルードと混同します。
Henley Chiu

19
内部的には、#include?まだループを実行していることに注意してください。ただし、コーダーはループを明示的に記述する必要がありません。ループせずに本当にタスクを実行する答えを追加しました。
Boris Stitnicky 2013

8
@HenleyChiu呼ばれた私[ 'Dog', 'Bird', 'Cat' ].has? 'Dog'

4
@AlfonsoVergaraはい、配列のソリューションでは内部で何らかのループを実行する必要があります。ループせずに配列のメンバーシップをテストする方法はありません。内部的にもループを実行したくない場合は、固定サイズのキーを持つ完全なハッシュテーブルなど、別のデータ構造を使用する必要があります。内部でループせずに配列のメンバーシップをテストする方法がないことを考えると、質問を「自分で明示的にループを記述する必要なしに」を意味すると解釈しました
Brian Campbell

250

あるin?方法は、ActiveSupport@campatersonにより指摘したように、V3.1以降(レールの一部)。Rails内で、またはの場合はrequire 'active_support'、次のように記述できます。

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

OTOH、Ruby コアのトップノッチメンバーである遠藤裕介によって以前に提案されたにもかかわらず、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?そのコアには含まれていませんが、Railsを使用している場合は使用できます。api.rubyonrails.org/classes/Object.html#method-i-in-3F(これはRubyであり、Railsの質問ではありませんが、Railsでの使用#in?を検討している人には役立つかもしれません。Railsに追加されたようです3.1 apidock.com/rails/Object/in%3F
campeterson

166

試す

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

これは古い構文です。^^^見てください
@brian

62
@jahrichieこの回答では、「古い構文」、オプションの括弧を正確にどう思いますか?
デニス

3
私は@Dennisに同意します、これは古いものではありません。括弧はオプションであり、ほとんどの場合、良い慣行です。角かっこを使用する必要があるかどうか(「古い」ルビ構文とはまったく関係ありません)
d1jhoni1b 2018

これが、3項演算で機能する唯一の構文です。
マイケルキャメロン

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)です。


49

ブロックごとにチェックしたい場合は、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

43

Rubyには、配列内の要素を見つけるための11のメソッドがあります。

推奨されるのは、include?または、繰り返しアクセスする場合はセットを作成してから、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要素が内部rb_equal_opt/rb_equal関数と一致すると中断する内部でC言語ループを使用します。メンバーシップチェックを繰り返すためのセットを作成しない限り、効率を上げることはできません。

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

両方とも、配列が最初に出現する期待値を探すため、O(n)時間の複雑さinclude?member?持っています。

Setを使用してO(1)アクセス時間を取得できますが、最初に配列のハッシュ表現を作成する必要があります。同じアレイのメンバーシップを繰り返し確認する場合、この初期投資はすぐに報われることがあります。SetCには実装されていませんが、単純な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インスタンスを作成し、すべてのオブジェクトをマッピングし、HashクラスのO(1)アクセス時間で実装されているtrueメンバーシップをチェックしますHash#include?

他の7つの方法は効率が悪いため、説明しません。

上記の11よりも複雑なO(n)のメソッドは実際にはさらに多くありますが、最初の一致でブレークするのではなく配列全体をスキャンするため、リストしないことにしました。

これらは使用しないでください。

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

Rubyには何でもできる正確な11の方法があると言うのはなんて勇敢なことでしょう。あなたが#12を逃し、次に#13を逃したなどと誰かが指摘するだろうとあなたが言うのと同じぐらいです。私の主張をするために、私は他の方法を提案しますが、最初に11あなたが列挙した方法について質問させてください。1つ目は、indexand find_index(またはfindand detect)を別々のメソッドとして数えることはほとんどできません。これらは同じメソッドの単なる異なる名前であるためです。第二に、で終わるすべての表現> 0が間違っています。これは間違いだったと思います。(続き)
Cary Swoveland

... arr.index(e)、たとえば、0ifを返しますarr[0] == earr.index(e)返品がないnil場合eは返品を呼び戻します。index1が探している場合は、しかし、使用することはできませんnilの中でarr。(rindexリストされていないと同じ問題。)配列をセットに変換してから、setメソッドを使用するのは少し難しいです。次に、ハッシュ(配列のキーと任意の値を使用)に変換してから、ハッシュメソッドを使用しないのはなぜですか。セットへの変換で問題がなくても、など、使用できる他のセットメソッドがあります!arr.to_set.add?(e)。(続き)
Cary Swoveland 2018

約束通り...、ここに使用することができ、さらにいくつかの方法があります:arr.count(e) > 0arr != arr.dup.delete(e) arr != arr - [e]arr & [e] == [e]selectとを採用することもできrejectます。
Cary Swoveland

重複が許可されていないという基準で特定の要素がリストに存在するかどうかを知っている場合は、セットを使用することを強くお勧めします。セットは非常に高速です。検索が必要な場合は、配列を実際に使用しないでください。それらは、順番に処理するために一時的に保管されるキューとして使用する方が適切です。ハッシュとセットは、何かが存在するかどうかを確認するのに適しています。
Tin Man

30

いくつかの回答が示唆Array#include?していますが、重要な警告が1つ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;
}

ループなしで単語の存在をテストする方法は、配列のトライを作成することです。トライの実装は数多くあります(google "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

8
a.each do ... end
うーん

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

11
トライの作成と維持のコストも同じくらいです。配列に対して多くの検索操作を実行している場合、トライにデータを入力して維持するためのメモリと時間のコストはそれだけの価値がありますが、1つまたは数百または数千のチェックの場合は、O(n)が完全に適しています。依存関係の追加を必要としない別のオプションは、配列をソートするか、ソートされた順序で維持することです。この場合、バイナリ検索O(lg n)操作を使用して、包含をチェックできます。
スピーキング

1
@speakingcode、あなたは実用的な観点から正しいかもしれません。しかし、OPは「値が存在するかどうかを確認し、ループすることなく、それ以上のものはない」と要求します。私がこの答えを書いたとき、ここには多くの実用的な解決策がありましたが、実際に質問者の文字通りの要件を満たすものはありませんでした。分探索木を試みに関連していることをあなたの観察は正しいですが、文字列のため、トライは仕事のための適切なツールである、でもウィキペディアには、その多くを知っています。適切に実装されたトライを構築および維持する複雑さは、驚くほど有利です。
Boris Stitnicky 2014

18

ループしたくない場合は、配列でそれを行う方法はありません。代わりに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

欠点は、セットキーとハッシュキーに一意のアイテムのみを含めることができ、多数のアイテムを追加した場合、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)

検出を使用すると、少なくともループを減らすことができます。検出は、最初の項目が「検出」されると停止します(項目に渡されるブロックはtrueと評価されます)。さらに、何も検出されない場合に何をすべきかを検出に通知することができます(ラムダで渡すことができます)。
aenw 14

1
@aenwはinclude?最初のヒットで停止しませんか?
Kimmo Lehto 2014

あなたは絶対的に正しいです。includeについて忘れていたので、detectを使用することに慣れています。あなたのコメントをありがとう-それは私の知識をリフレッシュすることを確実にしました。
aenw 2014

include?最初のヒットで停止しますが、そのヒットがリストの最後にある場合...ストレージ用に配列に依存するソリューションは、リストが大きくなるにつれて、特に要素の最後に要素を見つける必要がある場合に、パフォーマンスが低下します。リスト。ハッシュとセットにはその問題はなく、順序付きリストと二分探索もありません。
ティンマン

それはそもそもこの答えが何であったかということです:)
Kimmo Lehto

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それでも配列を反復処理し、要素の値を返すだけです。
ティンマン

13

楽しい事実、

を使用*して、case式の配列メンバーシップを確認できます。

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

*when句の小さな部分に注意してください。これにより、配列のメンバーシップがチェックされます。

splat演算子の通常のすべての魔法の動作が適用されます。たとえば、array実際には配列ではなく単一の要素である場合、その要素と一致します。


知っておきたい
Cary Swoveland

また、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?Array#include

時間の複雑さO(1)演算O(n)演算 

アクセスタイプが各要素を反復処理する場合、ハッシュ[キー]にアクセスします
                        それまでの配列の値を返します
                        配列内の値を検索すると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 2017年

おい私はループ内でインクルードを使用しないと言っていました、インクルードを使用した1回のチェックには問題ありません ですから、最初にユースケースを読んでください。ループ内で複数回チェックする必要がある場合は、より良い方法です。
aqfaridi 2017年

配列をハッシュに変換するにはコストがかかりますが、配列を検索に使用するのは間違った構造です。配列は、順序が重要になる可能性があるキューとして優れています。包含/存在/一意性が重要な場合は、ハッシュとセットの方が優れています。適切なコンテナから始めれば、問題は解決されます。
ティンマン

4

これは、それが存在することだけでなく、それが何回出現するかも通知します。

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

12
何回出現するかを知りたいのでなければ、これを使用しても意味がありません。ただし、.any?最初に一致する要素が見つかるとすぐに返されるため、.count常に配列全体が処理されます。
Zaz 14

これは技術的には何かが存在するかどうかを通知しますが、速度を重視する場合は適切な方法ではありません
ティンマン

4

価値のあることとして、Rubyのドキュメントは、この種の質問に対するすばらしいリソースです。

また、検索している配列の長さに注意します。このinclude?メソッドは、配列のサイズによってはかなり醜くなる可能性のあるO(n)の複雑さで線形検索を実行します。

大規模な(並べ替えられた)配列を使用している場合は、難しくないはずのO(log n)の最悪の場合のバイナリ検索アルゴリズムを作成することを検討します。

または、Ruby 2.0を使用している場合は、を利用できますbsearch


3
二分探索は、配列がソートされている(または何らかの形式で順序付けられている)ことを前提としているため、大規模な配列ではコストがかかり、多くの場合、利点が打ち消されます。
ティンマン

二分探索では、要素のすべてのペアがと比較可能であることも必要<=>ですが、常にそうであるとは限りません。たとえば、配列の要素がハッシュだったとします。
Cary Swoveland

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?同じです。

これは1行で作業できます。


3

これを使用したくない場合include?も機能します:

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

3
どれか?ブロックも受け入れます:['cat'、 'dog'、 'horse']。any?{| x | x == 'dog'}
maikonas

3
['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見つかりましたか?
Cary Swoveland

2

この方法はどうですか?

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

要素を見つけるためだけに、配列を繰り返し処理します。次に、その要素のインデックスを返す必要があります。
Tin Man


2

これには別の方法があります。

配列が[ :edit, :update, :create, :show ]であると仮定します。おそらく、7つすべての致命的/静穏な罪です。

そして、いくつかの文字列から有効なアクションを引き出すというアイデアをさらに楽しみます:

"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 '29

正規表現は大丈夫です(rubular.comをチェック)-そして@Rambatino:なぜAmazon Echoのようになるのでしょう;)たとえば、「チキンをショッピングリストに追加してください」(そして-そうですね。 :addを配列に追加しますが、その要点は
わかり

1
「正規表現は大丈夫です(rubular.comで確認)」。それは大丈夫ではありません。正規表現は、文字列の最初または最後のキーワードとは一致しません(例:「兄弟のプロフィールを更新」)。先頭と末尾を一致させたくない場合でも、キーワードのどちらかの側の文字クラスは/[,. ]/
bkDJ

@bkDJが言うように、正規表現は間違っています。rubular.com/r/4EG04rANz6KET6
ブリキ男

1

trueまたはfalseだけでなく値を返したい場合は、

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

リストに存在する場合は「Dog」を返し、それ以外の場合はnilを返します。


または、true / false(値ではない)が必要であるが、このようなブロックと比較たいarray.any?{|x| x == 'Dog'}場合にも使用します。
mahemoff

0

これを行うもう1つの方法を次に示します。

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

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

1
これはこれを行うには恐ろしく非効率的な方法です!技術的に正しくないわけではないので、私は反対票を投じていません。誰かがRubyを読んで何かについて学ぶかもしれませんが、上記のより多くのより良い答えがあります。
ジベリア

コーンヘッド、あなたはに単純化することができますarr != arr - [e]arr & [e] == [e]同じ線に沿った別の方法です。
Cary Swoveland

@CarySwoveland魔法使いの帽子をからかわないでください;-)
ワンドメーカー

私は最も敬意を表して、帽子ではなくウィザードの頭を指していた。
Cary Swoveland


0

配列内の要素を見つける方法はたくさんありますが、最も簡単な方法は 'in?'です。方法。

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

2
注:この回答で説明されているように、メソッドin?ActiveSupportインポートする必要がありますrequire active_support
Patrick

コア拡張機能を使用する場合、すべてのActiveSupportは必要ありません。
Tin Man

0

使用したくないinclude?場合は、最初に要素を配列でラップしてから、ラップされた要素が配列とラップされた要素の共通部分に等しいかどうかを確認できます。これは、等しいかどうかに基づいてブール値を返します。

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

-1

何かを行うさまざまな方法の相対的な速度を確認するために、いくつかのベンチマークを実行することは常に興味深いことです。

開始、中間、または終了で配列要素を検索すると、線形検索に影響しますが、セットに対する検索にはほとんど影響しません。

配列をセットに変換すると、処理時間に影響が出るため、配列からセットを一度作成するか、最初からセットから始めます。

ベンチマークコードは次のとおりです。

# 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が小さいほど、Arrayメソッドの実行速度は速くなりますが、それでも追いつくことはできませんが、小さな配列では違いはわずかです。

"First"、 "Middle"、および "Last"はfirst、検索対象の要素に対する、size / 2およびlastforの使用を反映していますARRAY。その要素はARRAYおよびSET変数を検索するときに使用されます。

> 0テストは型テスト>= 0用でなければならないため、比較対象のメソッドに小さな変更が加えられましたindex

Fruityとその方法論の詳細については、READMEを参照してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.