Ruby에서 배열에 값이 있는지 확인하는 방법


1315

'Dog'과 배열이 ['Cat', 'Dog', 'Bird']있습니다.

배열을 반복하지 않고 배열에 존재하는지 어떻게 확인합니까? 값이 존재하는지 확인하는 간단한 방법이 있습니까?


5
.include를 사용 하시겠습니까? 방법 . 원하는 부울을 반환합니다. 귀하의 경우 [ 'Cat', 'Dog', 'Bird']. include ( 'Dog')를 입력하면 부울 true를 반환해야합니다.
Jwan622

include를 사용하지 않습니까? 포함하기 때문에 배열에 다른 값이 있는지 여러 번 확인하려면 방법? 매번 검색 할 때마다 O (n) 연산을 수행하는 배열을 반복 수행합니다. 대신 해시를 만들고 hash = arr.map {|x| [x,true]}.to_h이제 hash.has_key? 'Dog' 반환 여부를 확인합니다.
aqfaridi

3
당신은 정말로 그것을 "반복하지 않고"완전히 할 수는 없습니다. 논리적으로 불가능합니다. 컴퓨터는 요소를 반복하지 않고 배열에 요소가 포함되어 있는지 여부를 알 수 없으므로 검색하는 요소가 있는지 확인할 수 없습니다. 물론 비어 있지 않는 한. 그런 다음 루프가 필요하지 않은 것 같습니다.
Tim M.

배열과 집합에서 요소를 찾는 다양한 방법의 차이점에 대한 테스트는 아래 벤치 마크를 참조하십시오. stackoverflow.com/a/60404934/128421
Tin Man

답변:


1945

당신이 찾고있는 것 include?:

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

73
다른 구문 :%w(Cat Dog Bird).include? 'Dog'
scarver2

184
때때로 나는 그것이 포함되어 있지 않은 "포함"했으면 좋겠다. 나는 항상 포함과 섞여 있습니다.
Henley Chiu

19
내부적으로 #include?여전히 루핑을 수행 한다는 점에 유의하십시오 . 그러나 코더는 루프를 명시 적으로 작성하여 저장됩니다. 루프를 반복하지 않고 작업을 수행하는 답변을 추가했습니다.
Boris Stitnicky

8
이 불렀다 @HenleyChiu I[ 'Dog', 'Bird', 'Cat' ].has? 'Dog'

4
@AlfonsoVergara 그렇습니다. 어레이에 대한 솔루션은 내부적으로 어떤 종류의 루핑을 수행해야합니다. 루핑없이 어레이 멤버쉽을 테스트 할 방법이 없습니다. 내부에서도 루핑을 원하지 않으면 고정 크기 키가있는 완벽한 해시 테이블과 같은 다른 데이터 구조를 사용해야합니다. 내부적으로 루프하지 않고 어레이의 멤버쉽을 테스트 할 방법이 없다는 점을 감안할 때, "루프를 명시 적으로 작성하지 않고"라는 의미의 질문을 해석했습니다.
Brian Campbell

250

in?방법ActiveSupport@campaterson가 가리키는 아웃과 같은 버전 3.1 이후 (레일의 일부). 따라서 Rails 내에서 또는 다음 require 'active_support'과 같이 쓸 수 있습니다.

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

OTOH, Ruby 자체 에는 in연산자 나 #in?메소드 가 없습니다. 비록 이전에, 특히 Yusuke Endoh에 의해 제안되었지만 루비 코어의 최고 수준의 멤버 인 .

다른 사람에 의해 지적, 반대 방법이 include?존재하며, 모든 Enumerable포함의 Array, Hash, Set, Range:

['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 (나는 이것이 Rails의 질문이 아니라 Ruby라는 것을 알고 있지만 #in?Rails 에서 사용하려는 사람에게 도움이 될 수 있습니다 . Rails에 추가 된 것 같습니다. 3.1 apidock.com/rails/Object/in%3F
campeterson

166

시험

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

이것은 더 오래된 구문입니다. ^^^
@brian

62
@jahrichie이 답변에서 선택적인 괄호 인 "이전 구문"을 정확히 어떻게 생각하십니까?
Dennis

3
나는 @Dennis에 동의합니다. 이것은 더 오래된 것이 아니며 괄호는 선택 사항이며 대부분의 경우 좋은 연습 .... 예를 들어 문장이 있으면 한 줄에 괄호없이 include를 사용하십시오. 또는 (모두에서 "이전"루비 구문과 관련이없는) 괄호 여부를 사용해야한다
d1jhoni1b

이것은 삼항 연산 내에서 작동 할 수있는 유일한 구문입니다.
Michael Cameron

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 []와 transpose ops가 프로파일을 지배하고 각각 O (n) 그 자체이다.



43

루비에는 배열에서 요소를 찾는 11 가지 방법이 있습니다.

바람직한 하나는 include?반복 된 액세스에 대해 호출 후 creat에 세트하고, 또는 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함수 와 일치 할 때 중단됩니다 . 반복 멤버쉽 확인을위한 세트를 작성하지 않으면 훨씬 효율적으로 얻을 수 없습니다.

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) 액세스 시간을 얻을 수 있습니다. 동일한 어레이에서 멤버십을 반복해서 확인하면이 초기 투자가 빠르게 보상받을 수 있습니다. 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인스턴스를 만들고 모든 객체를 매핑 true한 다음 다음을 사용하여 멤버십을 확인합니다.Hash#include? 해시 클래스에서 O (1) 액세스 시간으로 구현 된 .

다른 7 가지 방법은 모두 덜 효율적이므로 다루지 않습니다.

실제로 위에 나열된 11 이상의 O (n) 복잡도를 가진 훨씬 더 많은 메소드가 있지만 첫 번째 일치를 깨지 않고 전체 배열을 스캔하기 때문에 나열하지 않기로 결정했습니다.

이것을 사용하지 마십시오 :

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

루비에는 정확히 11 가지 방법이 있습니다. 누군가가 당신이 # 12, # 13 등을 놓쳤다 고 지적 할 것입니다. 요점을 밝히기 위해 다른 방법을 제안하지만 먼저 11열거 한 방법에 대해 질문하겠습니다 . 첫째, 당신은 거의 셀 수 indexfind_index(나 find하고 detect는 동일한 방법 단지 다른 이름이기 때문에, 별도의 방법으로). 두 번째로 끝나는 모든 표현 > 0이 잘못되었습니다. 확실히 감독이었습니다. (계속)
Cary Swoveland

arr.index(e)예를 들어 ... 은 0if를 반환 합니다 arr[0] == e. 없는 경우 arr.index(e)반품 을 회수 합니다 . 하나가 검색되는 경우, 그러나, 사용할 수 없습니다 에서 . (와 동일한 문제이며 , 목록에 없습니다.). 배열을 세트로 변환 한 다음 세트 메소드를 사용하는 것은 약간의 확장입니다. 그렇다면 배열의 키와 임의 값을 사용하여 해시로 변환 한 다음 해시 방법을 사용하십시오. 집합으로 변환해도 괜찮은 경우와 같이 사용할 수있는 다른 집합 방법이 있습니다 . (계속)nileindexnilarrrindex!arr.to_set.add?(e)
Cary Swoveland

약속대로 ..., 여기에 사용될 수있는 몇 가지 방법은 다음과 같습니다 arr.count(e) > 0, arr != arr.dup.delete(e) , arr != arr - [e]arr & [e] == [e]. select그리고 또한 고용 할 수 있습니다 reject.
캐리 스월 랜드

기준이 중복이 허용되지 않고 특정 요소가 목록에 있는지 여부를 알고 있다면 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 를 구성하는 것 입니다. 거기에는 많은 트리 구현 (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 }

이제 우리는 sublinear를 사용하여 O(log n)구문상의 단순성으로 다음과 같은 시간에 반복하지 않고 배열에 다양한 단어의 존재를 테스트 할 준비가되었습니다 .Array#include?Trie#include?

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

8
a.each do ... end음 ... 루프가 아닌지 확실하지 않습니다
Doorknob

27
여기에는 실제로 루프가 포함되어 있습니다. O (1)이 아닌 것은 어떤 종류의 루프를 포함합니다. 입력 문자열의 문자를 반복합니다. 또한 Set#include?효율성에 관심이있는 사람들에게 이미 언급 된 답변보다 주목하십시오 . 문자열 대신 기호를 사용하여 결합하면 O (1) 평균 사례가 될 수 있습니다 (문자열을 사용하는 경우 해시 계산은 O (n)이며 여기서 n은 문자열의 길이입니다). 또는 타사 라이브러리를 사용하려면 O (1) 최악의 경우 인 완벽한 해시를 사용할 수 있습니다.
Brian Campbell

4
AFAIK Set는 해시를 사용하여 멤버를 인덱싱하므로 실제로 Set#include? 잘 분산 된 Set(더 구체적으로 해싱의 경우 O (input-size) 및 O (log (n / bucket-number))의 경우 O (1)이 복잡 해야 합니다. 검색)
Uri Agassi

11
트라이를 생성하고 유지하는 데 드는 비용은 매우 큽니다. 배열에서 많은 검색 작업을 수행하는 경우 trie를 채우고 유지 관리하는 데 드는 메모리 및 시간 비용은 가치가 있지만 단일 또는 수백 또는 수천 번의 검사에는 O (n)이 완벽하게 적합합니다. 종속성을 추가하지 않아도되는 다른 옵션은 배열을 정렬하거나 정렬 된 순서로 유지하는 것입니다.이 경우 이진 검색 O (lg n) 연산을 사용하여 포함을 확인할 수 있습니다.
speakingcode

1
@speakingcode, 당신은 실용적인 관점에서 옳을 수도 있습니다. 그러나 OP는 "루프없이 값이 존재하는지 여부를 확인"하도록 요청합니다. 이 답변을 썼을 때 여기에 실용적인 해결책이 많았지 만 실제로는 실제로 요구 자의 문자 요구 사항을 충족시키지 못했습니다. BST가 시도와 관련이 있다는 관찰은 정확하지만 문자열의 경우 trie가 작업에 적합한 도구이며 Wikipedia도 그 사실을 잘 알고 있습니다. 잘 구현 된 트라이를 구성하고 유지하는 복잡성은 놀랍도록 유리하다.
Boris Stitnicky

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

세트는 내부적으로 해시처럼 작동하므로, 루비는 이름에서 알 수 있듯이 키의 해시를 생성하고 각 해시가 메모리의 특정 지점을 가리 키도록 메모리 맵을 생성하므로 항목을 찾기 위해 컬렉션을 반복 할 필요가 없습니다. 이전 예제는 해시로 완료되었습니다.

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

단점은 세트 및 해시 키에는 고유 한 항목 만 포함 할 수 있으며 많은 항목을 추가하는 경우 Ruby는 특정 키 수를 초과 한 후 전체 키를 다시 해싱하여 더 큰 키 공간에 적합한 새 맵을 작성해야한다는 것입니다. 이에 대한 자세한 내용은 " LongWest RubyConf 2014 – 집에서 만든 해시의 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를 사용하면 최소한 루핑을 줄일 수 있습니다. detect는 첫 번째 항목 'detected'에서 중지됩니다 (항목에 대해 전달 된 블록은 true로 평가됨). 또한 아무것도 감지되지 않으면 어떻게해야하는지 감지 할 수 있습니다 (람다를 전달할 수 있음).
aenw

1
@aenw는 include?첫 번째 히트에서 멈추지 않습니까?
Kimmo Lehto

당신 말이 맞아요 나는 포함에 대해 잊어 버린 감지 기능을 사용하는 데 익숙합니다. 귀하의 의견에 감사드립니다-내 지식을 새로 고쳤습니다.
aenw

include?첫 번째 적중시 중지하지만 해당 적중이 목록의 끝에있는 경우 .... 스토리지를 위해 어레이를 사용하는 솔루션은 목록이 커짐에 따라 성능이 저하됩니다. 특히 명부. 해시 및 세트에는 그러한 문제가 없으며 정렬 된 목록과 이진 검색이 없습니다.
Tin Man

이 답변이 처음에 관한 것입니다 :)
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여전히 배열을 반복하고 요소의 값을 반환합니다.
Tin Man

13

재미있는 사실,

식에서 *배열 구성원을 확인 하는 데 사용할 수 있습니다 case.

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

조금 주목 *when 절의 에 . 그러면 배열의 멤버쉽이 검사됩니다.

splat 연산자의 모든 일반적인 마술 동작이 적용되므로 예를 들어 array실제로 배열이 아니라 단일 요소 인 경우 해당 요소와 일치합니다.


알아 둘만 한..!
캐리 스월 랜드

또한 case 문에서 첫 번째 검사가 느릴 수도 있으므로 마지막으로 when가능한 빨리 사용하여 다른 빠른 검사를 신속하게 처리 할 수 ​​있습니다.
Tin Man

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?에만 레일 (즉, 추가되었습니다 ActiveSupport) 버전 3.1 +. 핵심 Ruby에서는 사용할 수 없습니다.
Tom Lord

5

당신이 어떤 키 변환을위한 배수에게 시간을 확인해야하는 경우 arrhashO (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

해시 #has_key의 성능은 ? 배열 #include?

매개 변수 해시 #has_key? 배열 #include

시간 복잡성 O (1) 연산 O (n) 연산 

액세스 유형 각 요소를 반복하는 경우 해시 [키]에 액세스
                        배열의 값을 반환 할 때까지
                        배열의 값을 찾으면 true가 반환됩니다.
                        해시 #has_key? 요구
                        요구    

일회성 점검의 경우에는 사용 include?이 좋습니다


배열을 반복하여 해시로 변환하지 않습니까?
chrisjacob

2
네,하지만 지금은 배열에 어떤 요소가 있는지 여부를 확인하기 위해 미리 계산 된 답변을 받았습니다. 다음 번에 다른 단어를 검색하면 사전 계산에는 O (n)이 걸리지 만 쿼리에는 O (1)이 필요합니다. 실제로 나는 include를 사용 했습니까? 배열 여부에 특정 요소의 존재, 그것은 성능 유적 여부를 확인하기위한 루프 안에 내 응용 프로그램에서, 그것은 병목 현상이, 루비 교수 체크
aqfaridi

1
.... 그러나 O (n) 공간과 O (n) 시간도 없습니다. @ chris72205가 올바르게 지적했듯이 배열을 먼저 반복해야하기 때문입니다. O (1) + O (n) = O (n)입니다. 실제로 이것은 포함하는 것보다 더 나쁩니 까?
Rambatino

내가 방금 내부 루프를 사용하지 말라고 말했어, include를 사용하여 단일 시간 검사를하는 것이 좋습니다. 따라서 유스 케이스를 먼저 읽으십시오. 루프 내부에서 여러 번 확인 해야하는 경우 더 좋습니다.
aqfaridi 2016 년

배열을 해시로 변환하는 것은 비용이 많이 들지만 검색에 배열을 사용하는 것은 잘못된 구조입니다. 배열은 순서가 중요한 대기열보다 좋습니다. 포함 / 존재 / 고유성이 중요 할 때 해시 및 세트가 더 좋습니다. 올바른 용기로 시작하면 문제가 발생합니다.
Tin Man

4

이것은 그것이 존재한다는 것뿐만 아니라 그것이 몇 번이나 나타나는지를 알려줍니다.

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

12
.any?첫 번째 일치하는 요소를 찾은 즉시 반환되므로 .count항상 전체 배열을 처리 하므로 표시되는 횟수를 알고 싶지 않으면 이것을 사용하는 것은 의미가 없습니다 .
Zaz

이것은 기술적으로 무언가 존재하는지 여부를 알려주지 만 속도에 관심이 있다면 올바른 방법이 아닙니다 .
틴 남자

4

가치있는 것에 대해, 루비 문서 는 이런 종류의 질문에 대한 놀라운 자료입니다.

또한 검색하는 배열의 길이를 기록합니다. 이 include?방법은 배열의 크기에 따라 O (n) 복잡도를 가진 선형 검색을 실행하여 상당히 추악해질 수 있습니다.

큰 (정렬 된) 배열로 작업하는 경우 너무 어렵지 않아야하며 최악의 O (log n)를 갖는 이진 검색 알고리즘 을 작성하는 것이 좋습니다.

또는 Ruby 2.0을 사용하는 경우을 이용할 수 있습니다 bsearch.


3
이진 검색은 배열이 정렬 (또는 어떤 형태로 정렬)되어 있다고 가정하는데, 이는 큰 배열에 비용이 많이 들며 종종 이점을 무시합니다.
Tin Man

이진 검색은 또한 모든 요소 쌍이와 비교 가능해야 <=>하지만 항상 그런 것은 아닙니다. 예를 들어 배열의 요소가 해시라고 가정합니다.
캐리 Swoveland

1
@CarySwoveland 정렬 된 배열 내의 요소가 비교 가능하다는 것을 암시해야합니다.
davissp14

4

당신은 시도 할 수 있습니다:

예 : 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?동일합니다.

이것은 한 줄로 작업을 수행 할 수 있습니다!


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발견?
캐리 Swoveland

2

이 방법은 어떻습니까?

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

요소를 찾기 위해 여전히 배열을 반복합니다. 그런 다음 해당 요소의 인덱스를 반환해야합니다.
Tin Man


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'이 있으면 그렇지 않으면 nil을 반환합니다.


또는 사용 array.any?{|x| x == 'Dog'}당신이 경우 않는 참 / 거짓의 (값이 아닌)을 원하지만이 같은 블록에 대해 비교하려는.
mahemoff 2016 년

0

이를 수행하는 또 다른 방법은 다음과 같습니다.

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

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

1
이것은 끔찍하게 비효율적 인 방법입니다! 기술적으로 부정확하지 않기 때문에 downvoting하지 않으며 누군가 Ruby에서 읽은 것을 배울 수는 있지만 위의 더 나은 답변이 많이 있습니다.
jibberia

Conehead,으로 단순화 할 수 있습니다 arr != arr - [e]. arr & [e] == [e]같은 줄을 따라 또 다른 방법입니다.
캐리 스월 랜드

@CarySwoveland 마법사의 모자를 놀리지 마라 ;-)
완드 메이커

나는 모자가 아닌 마법사의 머리를 가장 존중하는 것으로 언급하고있었습니다.
캐리 스월 랜드


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를 사용하지 말고 Set을 사용하십시오. (또는 해시)

Array가 작을수록 Array 메서드가 더 빨리 실행되지만 작은 배열에서는 차이가 작을 수 있지만 여전히 유지되지는 않습니다.

"첫째", "중간"과 "마지막"의 사용을 반영 first, size / 2그리고 last대한 ARRAY의 요소 존재는 검색. 해당 요소는 ARRAYSET변수를 검색 할 때 사용됩니다 .

> 0테스트는 형식 테스트에 >= 0대한 것이므로 비교하는 방법에 대해 약간 변경되었습니다 index.

Fruity와 그 방법론에 대한 자세한 내용은 README를 참조하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.