Ruby'deki bir dizide bir değerin olup olmadığını kontrol etme


1315

Bir değer 'Dog've bir dizi var ['Cat', 'Dog', 'Bird'].

Dizide döngü olmadan var olup olmadığını nasıl kontrol edebilirim? Değerin olup olmadığını kontrol etmenin basit bir yolu var mı?


5
kullanın .include? yöntemi . İstediğiniz bir boolean döndürür. Sizin durumunuzda şunu yazın: ['Cat', 'Dog', 'Bird']. İnclude ('Dog') ve boolean true değerini döndürmelidir.
Jwan622

kullanmayın dahil? Eğer dizide mevcut farklı değer için katları kez kontrol etmek istiyorsanız yöntemi içerir çünkü dahil? her seferinde her seferinde arama yapmak için O (n) işlemini alarak dizi üzerinden hash = arr.map {|x| [x,true]}.to_hhash.has_key? 'Dog'
yinelenir

3
Bunu gerçekten "döngü yapmadan" yapamazsınız. Mantıken imkansızdır, bilgisayar, dizinin öğeyi içerip içermediğini, aradığı öğenin olup olmadığını kontrol etmek için öğelerin arasında dolaşıp dolaşmadığından emin olamaz. Tabii boş değilse. O zaman sanırım bir döngüye ihtiyacınız yok.
Tim M.

Bir Dizi ve Küme içindeki bir öğeyi bulmanın çeşitli yollarının farklarının testleri için aşağıdaki kıyaslamalara bakın. stackoverflow.com/a/60404934/128421
Tin Man

Yanıtlar:


1945

Şunu arıyorsunuz include?:

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

73
Alternatif sözdizimi:%w(Cat Dog Bird).include? 'Dog'
scarver2

184
Bazen keşke "içerir" olsaydı. Her zaman içerir ile karıştırılır.
Henley Chiu

19
Sadece dahili olarak #include?hala döngü gerçekleştirdiğini not edeyim . Ancak kodlayıcı, döngüyü açıkça yazmaktan saklanır. Görevi gerçekten döngü olmadan gerçekleştiren bir cevap ekledim.
Boris Stitnicky

8
@HenleyChiu I denilen[ 'Dog', 'Bird', 'Cat' ].has? 'Dog'

4
@AlfonsoVergara Evet, bir dizi için herhangi bir çözüm dahili olarak bir tür döngü yapmalıdır; döngü olmadan bir dizinin üyeliğini test etmenin bir yolu yoktur. Dahili olarak bile bir döngü yapmak istemiyorsanız, sabit boyutlu anahtarlara sahip mükemmel bir karma tablo gibi farklı bir veri yapısı kullanmanız gerekir. Dahili bir döngü olmadan bir dizide üyeliği test etmenin bir yolu olmadığı göz önüne alındığında, soruyu "döngüyü kendim yazmak zorunda kalmadan" anlamında yorumladım
Brian Campbell

250

Bir vardır in?yöntem içinde ActiveSupport@campaterson ile sivri dışarı olarak v3.1 yana (Rayların parçası). Yani Rails içinde, ya da sen require 'active_support', sen yazabilirsin:

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

OTOH, daha önce önerilmiş olmasına rağmen, Ruby'de, özellikle yakut çekirdeğin birinci sınıf üyesi Yusuke Endoh tarafından hiçbir inoperatör veya #in?yöntem yoktur .

Diğerleri tarafından işaret edildiği gibi, ters yöntem include?tüm için var Enumerabledahil sArray , Hash, Set, Range:

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

Dizinizde çok sayıda değer varsa bunların hepsinin birbiri ardına kontrol edileceğini unutmayın (ör. O(n) ), bir karma aramanın sabit zaman (yani O(1)) olacağını unutmayın . Örneğin, dizi sabitse, bunun yerine bir Set kullanmak iyi bir fikirdir . Örneğin:

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

Bir hızlı testi bu çağrı ortaya koymaktadırinclude? 10 elemana Seteşdeğer üzerinde çağırmak daha hızlı 3.5x hakkındadırArray (eleman bulunmazsa).

Son bir kapanış notu: include?a kullanırken dikkatli olun Range, incelikler var, bu yüzden dokümana bakın vecover? ...


2
Ruby #in?çekirdeğinde yer almasa da, Rails kullanıyorsanız, kullanılabilir. api.rubyonrails.org/classes/Object.html#method-i-in-3F (Bunun bir Ruby olduğunu, Rails sorusu olmadığını biliyorum, ancak Rails'te kullanmak isteyen herkes için yardımcı olabilir. Rails'e#in? eklendi gibi görünüyor. 3.1 apidock.com/rails/Object/in%3F
campeterson

166

Deneyin

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

bu eski sözdizimi, bak ^^^ @
brian'ın

62
@jahrichie Bu cevabın tam olarak ne olduğunu "eski sözdizimi" olarak değerlendiriyorsunuz, isteğe bağlı parantezler?
Dennis

3
ben katılıyorum @Dennis, bu eski değil, parantez isteğe bağlıdır ve çoğu durumda İYİ BİR UYGULAMA .... cümle örneğin bir satırda parantez olmadan dahil kullanmayı deneyin, ne demek istediğine göre parantez kullanmalı veya kullanmamalı (hiç "eski" yakut sözdizimi ile ilgili değil)
d1jhoni1b

Üçlü bir işlemde çalışabildiğim tek sözdizimi bu.
Michael Cameron

49

Kullanım Enumerable#include:

a = %w/Cat Dog Bird/

a.include? 'Dog'

Ya da, bir dizi test yapılırsa, 1 döngüden (hatta include?sahip olan) kurtulabilir ve O (n) ' den O (1)' e gidebilirsiniz :

h = Hash[[a, a].transpose]
h['Dog']


1. Umarım bu açıktır ama itirazları ortadan kaldırmak için: evet, sadece birkaç arama için Hash [] ve devrik ops profile hakimdir ve her biri O (n) 'dir .


48

Bir blokla kontrol etmek isterseniz any?veya seçeneğini deneyebilirsiniz 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  

Bkz Enumerable fazla bilgi için .

Benim ilham kaynağım " dizi yakut herhangi bir öğe olup olmadığını değerlendirmek "


1
Bu dizenin herhangi birinin / hepsinin başka bir dizede / sabitte bulunduğunu kontrol etmek istiyorsanız çok yararlı
thanikkal

43

Ruby'nin bir dizideki öğeleri bulmak için on bir yöntemi vardır.

Tercih edilen include?veya tekrarlanan erişim için bir Set oluşturup ardından include?veya öğesini çağırır member?.

İşte hepsi:

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Öğe varsa, hepsi bir ish değeri döndürür .

include?tercih edilen yöntemdir. forBir öğe dahili rb_equal_opt/rb_equalişlevlerle eşleştiğinde kırılan dahili olarak bir C dili döngüsü kullanır . Tekrarlanan üyelik kontrolleri için bir Set oluşturmazsanız çok daha verimli olamaz.

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?Arraysınıfta yeniden tanımlanmaz ve Enumerablemodülden tam anlamıyla tüm öğeler arasında numaralandırılan optimize edilmemiş bir uygulama kullanır :

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 koduna çevrildiğinde bu aşağıdakilerle ilgilidir:

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

Hem include?ve member?yana O (n) zaman karmaşıklığı sahip hem beklenen değerin ilk oluşum için bir dizi arama.

İlk önce dizinin Hash temsilini oluşturmak zorunda kalmadan O (1) erişim süresini elde etmek için bir Set kullanabiliriz. Aynı dizideki üyeliği tekrar tekrar kontrol ederseniz, bu ilk yatırım hızlı bir şekilde ödenebilir. SetC dilinde değil, temel Ruby sınıfı olarak uygulanmış, yine de altta yatan O (1) erişim süresi@hash buna değer.

Set sınıfının uygulanması:

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 sınıfının yalnızca dahili bir @hashörnek oluşturduğunu görebileceğiniz gibi, tüm nesneleri eşler ve Hash sınıfında O (1) erişim süresi ile uygulanan trueüyeliği denetler Hash#include?.

Diğer yedi yöntemi daha az verimli oldukları için tartışmayacağım.

Aslında yukarıda listelenen 11'in ötesinde O (n) karmaşıklığına sahip daha fazla yöntem var, ancak ilk maçta kırmak yerine tüm diziyi taradıkları için bunları listelememeye karar verdim.

Bunları kullanmayın:

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

Ruby'nin bir şey yapmanın tam olarak 11 yolu olduğunu söylemek ne kadar zalim! Birinin, # 12'yi, sonra # 13'ü kaçırdığınızı göstereceğini söylemeden daha erken. Demek istediğim, başka yollar önereceğim, ama önce 11sıraladığınız yolları sorgulayayım . İlk olarak, aynı yöntem için sadece farklı isimler olduklarından indexve find_index(veya findve detect) ayrı yöntemler olarak zorlukla sayabilirsiniz . İkincisi, ile biten tüm ifadeler > 0yanlış, ki bu bir gözetim olduğundan eminim. (devamı)
Cary Swoveland

... arr.index(e), örneğin, 0if değerini döndürür arr[0] == e. Mevcut değilse arr.index(e)iadeleri hatırlayacaksınız . biri arıyorsa, ancak, kullanılamaz içinde . ( Listelenmeyen ile aynı sorun .). Diziyi bir kümeye dönüştürmek ve sonra küme yöntemlerini kullanmak biraz esnektir. Neden bir karma değerine (dizideki anahtarlar ve rastgele değerler ile) dönüştürmüyorsunuz, sonra karma yöntemlerini kullanmıyorsunuz? Bir kümeye dönüştürmek uygun olsa bile, kullanılabilecek başka küme yöntemleri de vardır . (devamı)nileindexnilarrrindex!arr.to_set.add?(e)
Cary Swoveland

Söz verdiğimiz gibi ... burada, kullanılabilecek birkaç yöntem vardır: arr.count(e) > 0, arr != arr.dup.delete(e) , arr != arr - [e]ve arr & [e] == [e]. Bir de istihdam edilebilir selectve reject.
Cary Swoveland

Ölçütlerin yinelenmesine izin verilmemesi ve listede belirli bir öğenin var olup olmadığını bilmek bir Set kullanmanızı şiddetle tavsiye ederim . Set SOooo çok daha hızlı. Diziler, arama gerektiğinde gerçekten kullanılmamalıdır; İşlerin sırayla işlenmek üzere geçici olarak depolandığı bir kuyruk olarak daha iyi kullanılırlar. Hash ve Set, bir şey olup olmadığını görmek için daha iyidir.
Kalay Adam

30

Birkaç cevap öne sürüyor Array#include?, ancak önemli bir uyarı var: Kaynağa bakmak, hatta Array#include?döngü yapıyor:

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;
}

Kelime varlığını döngü olmadan test etmenin yolu diziniz için bir üçlü yapı oluşturmaktır . Orada birçok trie uygulaması vardır (google "ruby trie"). rambling-trieBu örnekte kullanacağım :

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 }

Ve şimdi dizinizdeki çeşitli kelimelerin varlığını, döngü içinde olmadan, O(log n)zaman içinde, alt Array#include?doğrusal kullanarak aynı sözdizimsel basitlikle test etmeye hazırız Trie#include?:

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

8
a.each do ... endUmm ... bunun bir döngü olmadığından emin değilim
Doorknob

27
Bunun aslında bir döngü içerdiğini unutmayın; O (1) olmayan her şey bir çeşit döngü içerir. Sadece giriş dizesinin karakterleri üzerinde bir döngü olur. Ayrıca Set#include?verimlilikten endişe duyan insanlar için daha önce bahsedilen bir yanıta dikkat edin ; dizeler yerine semboller kullanıldığında, ortalama O (1) büyük / küçük harf olabilir (dizeleri kullanırsanız, yalnızca karma değerini hesaplamak O (n) 'dir, burada n dizgenin uzunluğudur). Veya üçüncü taraf kütüphaneleri kullanmak istiyorsanız, O (1) en kötü durum olan mükemmel bir karma kullanabilirsiniz.
Brian Campbell

4
AFAIK, Setüyelerini endekslemek için karma kullanır, bu nedenle karma için iyi dağıtılmış (daha spesifik olarak O (giriş boyutu) ve O (log (n / kova numarası)) için karmaşık O (1) Set#include? olmalıdırSet . arama)
Uri Agassi

11
Üçgeni yaratmanın ve sürdürmenin maliyeti de aynı. Dizide birçok arama işlemi yapıyorsanız, bir üçgeni doldurmanın ve onu korumanın bellek ve zaman maliyeti buna değer, ancak tek, hatta yüzlerce veya binlerce kontrol için, O (n) mükemmel bir şekilde uygundur. Bağımlılık eklemeyi gerektirmeyen başka bir seçenek de diziyi sıralamak veya sıralı sıraya göre tutmak olabilir; bu durumda, dahil etmeyi kontrol etmek için bir ikili arama O (lg n) işlemi kullanılabilir.
konuşma

1
@ konuşma kodu, pragmatik açıdan doğru olabilirsiniz. Ancak OP "değerin var olup olmadığını kontrol etmeyi ister, daha fazla bir şey, döngü olmadan". Bu yanıtı yazdığımda, burada birçok pragmatik çözüm vardı, ama sorucunun gerçek ihtiyacını karşılayacak hiçbir şey yoktu. BST'lerin denemelerle ilgili olduğu gözleminiz doğrudur, ancak dizeler için trie iş için doğru araçtır, Wikipedia bile çok şey biliyor . İyi uygulanmış bir üçeri inşa etmenin ve sürdürmenin karmaşıklığı şaşırtıcı derecede elverişlidir.
Boris Stitnicky

18

Döngü yapmak istemiyorsanız, bunu Dizilerle yapmanın bir yolu yoktur. Bunun yerine bir Set kullanmalısınız.

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

Kümeler Hashes gibi dahili olarak çalışır, bu nedenle Ruby'nin öğeleri bulmak için koleksiyonda döngü yapmasına gerek yoktur, çünkü adından da anlaşılacağı gibi, anahtarların karmasını oluşturur ve her karmanın bellekteki belirli bir noktayı işaret etmesi için bir bellek haritası oluşturur. Hash ile yapılan önceki örnek:

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

Dezavantajı, Setler ve Hash anahtarlarının yalnızca benzersiz öğeleri içerebilmesidir ve çok fazla öğe eklerseniz, Ruby, daha büyük bir anahtar boşluğuna uyan yeni bir harita oluşturmak için belirli sayıda öğeden sonra her şeyi yeniden oluşturmanız gerekir. Bu konuda daha fazla bilgi için, " MountainWest RubyConf 2014 - Nathan Long tarafından bir Ev Yapımı Hash Big Big " izlemenizi öneririz .

İşte bir kıyaslama:

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

Ve sonuçlar:

      user     system      total        real
array  7.020000   0.000000   7.020000 (  7.031525)
set    0.010000   0.000000   0.010000 (  0.004816)

Tespit kullanırsanız, en azından döngüyü azaltabilirsiniz. tespit ilk tespit edilen öğede duracaktır (öğe için iletilen blok doğru olarak değerlendirilir). Ek olarak, hiçbir şey algılanmazsa ne yapılacağını tespit edebilirsiniz (bir lambda geçirebilirsiniz).
aenw

1
@aenw include?ilk vuruşta durmuyor mu?
Kimmo Lehto

kesinlikle haklısın. Algılamayı kullanmaya alışkın olduğumu unutmuştum. yorumunuz için teşekkürler - bilgimi yeniledim.
aenw

include?ilk isabette durur, ancak bu isabet listenin sonundaysa .... Depolama için bir Array'a dayanan herhangi bir çözüm, liste büyüdükçe, özellikle liste. Hash ve Set'in bu sorunu yok, sıralı bir liste ve ikili arama da yok.
Kalay Adam

Bu cevap ilk etapta neredeyse buydu :)
Kimmo Lehto

17

Bu, bunu yapmanın başka bir yoludur: Array#indexyöntemi kullanın .

Dizideki öğenin ilk oluşumunun dizinini döndürür.

Örneğin:

a = ['cat','dog','horse']
if a.index('dog')
    puts "dog exists in the array"
end

index() ayrıca bir blok alabilir:

Örneğin:

a = ['cat','dog','horse']
puts a.index {|x| x.match /o/}

Bu, dizideki 'o' harfini içeren ilk sözcüğün dizinini döndürür.


indexhala dizi üzerinden yinelenir, sadece öğenin değerini döndürür.
Tin Man

13

Eğlenceli gerçek,

İfadelerde *dizi üyeliğini kontrol etmek için kullanabilirsiniz case.

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

*When yan tümcesinde küçük fark edin , bu dizi üyeliği kontrol eder.

Uyarıcı operatörünün her zamanki sihir davranışı geçerlidir, örneğin arraygerçekte bir dizi değil, tek bir elemansa bu elemanla eşleşir.


Bunu bildiğim iyi oldu..!
Cary Swoveland

Ayrıca bir vaka ifadesinde yavaş bir ilk kontrol olurdu, bu yüzden whenmümkün olan en son , daha hızlı kontroller, hızlı bir şekilde ayık olsun kullanabilirsiniz.
Tin Man

9

Bunu başarmanın birçok yolu vardır. Bunlardan birkaçı aşağıdaki gibidir:

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

2.in? a  #=> true

8.in? a #=> false

a.member? 1 #=> true

a.member? 8 #=> false

6
Bunun Object#in?yalnızca Rails (yani ActiveSupport) v3.1 + 'a eklendiğine dikkat edin . Temel Ruby'de mevcut değildir.
Tom Lord

5

Eğer herhangi bir anahtar dönüştürmek için katları kere kontrol gerekiyorsa arriçin hashO (1) kontrol edin şimdi, ve

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 performansı ? Array # 'a karşı ?

Parametre Hash # has_key? Dizi # include

Zaman Karmaşıklığı O (1) işlemi O (n) işlemi 

Erişim Türü Her öğede yineliyorsa Karma [anahtar] 'a erişir
                        dizinin herhangi bir değerini, diziye kadar döndürür
                        true, Array'daki değeri bulur
                        Hash # has_key? aramak
                        aramak    

Tek seferlik kontrol include?kullanımı iyidir


Hala bir hash dönüştürmek için dizi döngü değil mi?
chrisjacob

2
evet, yaptım ama şimdi ben dizi herhangi bir eleman olup olmadığını kontrol etmek için önceden hesaplanmış cevap var. Şimdi, başka bir kelime aradığımda, ön hesaplama O (n) alacak olsa da, sorgu için O (1) alacaktır. Aslında ben dahil kullandım? benim dizi içinde benim uygulama içinde dizi belirli eleman olup olmadığını kontrol etmek için, bu performans mahveder, o ruby-prof kontrol, bu darboğaz oldu
aqfaridi

1
.... ama O (n) boşluk ve aynı zamanda hayır, O (n) zamanı, çünkü @ chris72205'in haklı olarak işaret ettiği gibi, önce dizinizi tekrarlamanız gerekir. O (1) + O (n) = O (n). Aslında bu, dahil etmekten çok daha mı kötü?
Rambatino

Ahbap sadece dahil kullanma döngü dahil demişti, dahil tek zamanlı kontrol için kullanma gayet iyi. Bu yüzden lütfen önce kullanım durumunu okuyun, döngü içinde birden çok kez kontrol etmeniz gerekiyorsa daha iyidir.
aqfaridi

Bir diziyi bir karmaya dönüştürmek pahalıdır, ancak arama için bir dizi kullanmak yanlış yapıdır. Diziler, düzenin önemli olabileceği kuyruklar kadar iyidir; İçerme / varoluş / teklik önemli olduğunda karmalar ve kümeler daha iyidir. Doğru kap ile başlayın ve sorun tartışmalıdır.
Tin Man

4

Bu size sadece var olduğunu değil, aynı zamanda kaç kez göründüğünü de söyleyecektir:

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

12
Kaç .any?eşleme göründüğünü bilmek istemiyorsanız bunu kullanmanın bir anlamı yoktur, ancak ilk eşleşen öğeyi bulur bulmaz geri döner .count, tüm diziyi her zaman işler.
Zaz

Bu teknik olarak bir şeyin var olup olmadığını söyleyecek olsa da , hızı önemsiyorsanız bunu yapmanın doğru yolu DEĞİLDİR .
Kalay Adam

4

Değerine göre , Ruby belgeleri bu tür sorular için harika bir kaynaktır.

Ayrıca aradığınız dizinin uzunluğunu da dikkate alırdım. include?Yöntem dizisinin boyutuna bağlı olarak oldukça çirkin almak O (n) karmaşıklığı olan bir doğrusal arama yapacaktır.

Büyük (sıralanmış) bir dizi ile çalışıyorsanız, çok zor olmamalı ve en kötü O (log n) durumuna sahip bir ikili arama algoritması yazmayı düşünürdüm .

Veya Ruby 2.0 kullanıyorsanız yararlanabilirsiniz bsearch.


3
İkili bir arama, dizinin sıralandığını (veya bazı biçimlerde sıralandığını) varsayar ve bu da büyük diziler için maliyetli olabilir ve genellikle avantajı reddeder.
Tin Man

İkili arama ayrıca tüm eleman çiftlerinin karşılaştırılabilir olmasını gerektirir <=>, bu her zaman böyle değildir. Örneğin, dizinin öğelerinin karma olduğunu varsayalım.
Cary Swoveland

1
@CarySwoveland, sıralı bir dizideki öğelerin karşılaştırılabilir olduğu az ya da çok ima edilmelidir.
davissp14

4

Deneyebilirsin:

Örnek: Dizide Cat ve Dog varsa:

(['Cat','Dog','Bird'] & ['Cat','Dog'] ).size == 2   #or replace 2 with ['Cat','Dog].size

Onun yerine:

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

Not: member?ve include?aynıdır.

Bu işi tek satırda yapabilir!


3

Kullanmamak istiyorsak include?bu da işe yarar :

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

3
hiç? ayrıca blokları da kabul eder: ['kedi', 'köpek', 'at']. {| 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. Oldu nilbulundu?
Cary Swoveland

2

Peki ya bu yol?

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

Hala öğeyi bulmak için dizi üzerinde yineleme yapacak. Sonra o öğenin dizinini döndürmek zorundadır.
Tin Man

2

Bunu bir MiniTest birim testinde yapmaya çalışıyorsanız kullanabilirsiniz assert_includes. Misal:

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

2

Bunun başka yolu var.

Dizinin, [ :edit, :update, :create, :show ]belki de yedi ölümcül / huzurlu günahın tamamı olduğunu varsayalım .

Ve bazı dize geçerli bir eylem çekme fikri ile daha fazla oyuncak :

"my brother would like me to update his profile"

Sonra:

[ :edit, :update, :create, :show ].select{|v| v if "my brother would like me to update his profile".downcase =~ /[,|.| |]#{v.to_s}[,|.| |]/}

1
Normal ifadeniz, /[,|.| |]#{v.to_s}[,|.| |]/'virgül, nokta, boşluk veya hiçbir şeyle çevrili eylemin adını' bulmak istediğinizi düşündürüyor, ancak bazı ince hatalar var. "|update|"dönecekti [:update]ve "update"dönecekti []. Karakter sınıfları ( [...]) |karakterleri ayırmak için dikey çizgi ( ) kullanmaz . Onları gruplar ( (...)) olarak değiştirsek bile boş bir karakterle eşleşemezsiniz. Muhtemelen istediğiniz regex/(,|\.| |^)#{v.to_s}(,|\.| |$)/
bkDJ

regex tamam (rubular.com'u kontrol etti) - ve @Rambatino: Neden Amazon Echo gibi olsun;) diyebilirsin: "lütfen alışveriş listeme tavuk ekle" (ve - iyi - o zaman ekleyin: diziye ekleyin, ama ben onun özünü almak sanırım;)
walt_die

1
msgstr "normal ifade ok (check rubular.com)". Tamam değil. Normal ifadeniz bir dizenin başında veya sonunda bir anahtar kelimeyle eşleşmez (örneğin "kardeşimin profilini güncelleyin"). Başlangıcı veya sonu eşleştirmek istemiyorsanız, /[,. ]/
regex'iniz

@BkDJ'nin dediği gibi, normal ifade yanlıştır. rubular.com/r/4EG04rANz6KET6
Tin Man

1

Değeri yalnızca doğru veya yanlış döndürmekle kalmaz,

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

Bu listede varsa 'Köpek' döndürür, aksi takdirde sıfır.


Ya kullanımı array.any?{|x| x == 'Dog'}eğer do doğru / yanlış (değil değer) istiyorum, ama aynı zamanda böyle bir blok karşı karşılaştırmak istiyorum.
mahemoff

0

İşte bunu yapmanın bir yolu daha:

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

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

1
Bu, bunu yapmanın korkunç derecede verimsiz bir yoludur! Ben küçülmüyorum çünkü teknik olarak yanlış değil ve birisi Ruby hakkında bir şeyler okuyarak öğrenebilir, ancak yukarıda çok daha iyi cevaplar var.
jibberia

Conehead, basitleştirebilirsiniz arr != arr - [e]. arr & [e] == [e]aynı hatlar boyunca başka bir yol.
Cary Swoveland

@CarySwoveland Sihirbazın şapkasını dalga geçmeyin ;-)
Wand Maker

Sihirbazın kafasına atıfta bulunuyordum, şapka değil, en büyük saygıyla.
Cary Swoveland


0

herhangi bir dizide bir öğe bulmanın birçok yolu vardır, ancak en basit yol 'in?' yöntem.

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

2
Not: anlatıldığı gibi bu cevap , yöntem in?gerektirir ActiveSupportithal edilecek: require active_support.
Patrick

Temel uzantıları kullanırsanız tüm ActiveSupport'u gerektirmez .
Tin Man

0

kullanmak include?istemiyorsanız, önce öğeyi bir diziye sarabilir ve ardından sarılmış öğenin dizinin ve sarılmış öğenin kesişimine eşit olup olmadığını kontrol edebilirsiniz. Bu, eşitliğe dayalı bir boole değeri döndürür.

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

-1

Bir şeyi yapmanın çeşitli yollarının göreli hızını görmek için her zaman bazı kriterler çalıştırmak ilginç buluyorum.

Başlangıç, orta veya bitişte bir dizi öğesi bulmak, doğrusal aramaları etkiler, ancak bir Kümeye karşı aramayı neredeyse hiç etkilemez.

Diziyi bir Kümeye dönüştürmek, işlem süresinde bir isabet yaratacaktır, bu nedenle Kümeyi bir Diziden bir kez oluşturun veya en baştan bir Kümeyle başlayın.

Kıyaslama kodu:

# 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 dizüstü bilgisayarımda çalıştırıldığında sonuç:

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

Temelde sonuçlar, ilk öğenin istediğim şey olduğunu garanti edemediğim sürece, içerme arayacaksam, her şey için bir Set kullanmamı söylüyor, ki bu pek olası değil. Bir hash içine öğeler eklerken bazı ek yükler var, ancak arama süreleri çok daha hızlı, bunun bir düşünülmesi gerektiğini düşünmüyorum. Yine, onu aramanız gerekirse, bir Array kullanmayın, bir Set kullanın. (Veya Karma.)

Dizi ne kadar küçükse, Dizi yöntemleri o kadar hızlı çalışır, ancak hala devam etmeyeceklerdir, ancak küçük dizilerde fark küçük olabilir.

"İlk", "Orta" ve "Son" kullanımını yansıtmak first, size / 2ve lastiçin ARRAYiçin eleman olmak için aradı. Bu öğe ARRAYve SETdeğişkenleri aranırken kullanılacaktır .

Küçük değişiklikler karşılaştırarak edildi yöntemleri için yapılmıştır > 0testi olmalıdır çünkü >= 0için indextip testlerin.

Meyveli ve metodolojisi hakkında daha fazla bilgi README'sinde bulunmaktadır .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.