Cara memeriksa apakah ada nilai dalam array di Ruby


1315

Saya memiliki nilai 'Dog'dan array ['Cat', 'Dog', 'Bird'].

Bagaimana cara memeriksa apakah ada dalam array tanpa mengulanginya? Apakah ada cara sederhana untuk memeriksa apakah nilainya ada, tidak lebih?


5
gunakan .include? Metode . Ia mengembalikan boolean yang Anda inginkan. Dalam kasus Anda cukup ketik: ['Kucing', 'Anjing', 'Burung'] .sertakan ('Anjing') dan itu harus mengembalikan boolean benar.
Jwan622

jangan gunakan termasuk? metode jika Anda ingin memeriksa beberapa kali untuk nilai yang berbeda untuk hadir dalam array atau tidak karena termasuk? setiap kali akan beralih pada array yang mengambil operasi O (n) untuk mencari setiap kali, Alih-alih membuat hash hash = arr.map {|x| [x,true]}.to_h, sekarang periksa apakah hash.has_key? 'Dog' mengembalikan true atau tidak
aqfaridi

3
Anda tidak dapat benar-benar melakukannya sepenuhnya "tanpa mengulanginya". Secara logis tidak mungkin, komputer tidak dapat mengetahui dengan pasti apakah array berisi elemen tanpa melalui elemen untuk memeriksa apakah ada di antara mereka yang dicari. Kecuali kalau itu kosong, tentu saja. Maka saya kira Anda tidak perlu loop.
Tim M.

Lihat tolok ukur di bawah ini untuk pengujian perbedaan berbagai cara untuk menemukan elemen dalam Array dan Set. stackoverflow.com/a/60404934/128421
Tin Man

Jawaban:


1946

Anda sedang mencari include?:

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

73
Sintaks alternatif:%w(Cat Dog Bird).include? 'Dog'
scarver2

184
Terkadang saya berharap itu "mengandung" tidak termasuk. Saya selalu mencampuradukkannya dengan termasuk.
Henley Chiu

19
Biarkan saya perhatikan bahwa secara internal, #include?masih melakukan perulangan. Coder disimpan dari penulisan loop secara eksplisit. Saya telah menambahkan jawaban yang melakukan tugas dengan benar tanpa perulangan.
Boris Stitnicky

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

4
@ AlfonsoVergara Ya, solusi untuk array harus melakukan semacam perulangan secara internal; tidak ada cara untuk menguji keanggotaan array tanpa perulangan. Jika Anda tidak ingin melakukan pengulangan apa pun secara internal, Anda perlu menggunakan struktur data yang berbeda, seperti tabel hash yang sempurna dengan kunci berukuran tetap. Mengingat bahwa tidak ada cara untuk menguji keanggotaan dalam array tanpa mengulang secara internal, saya menafsirkan pertanyaan itu berarti "tanpa harus menulis loop sendiri"
Brian Campbell

250

Ada in?metode di ActiveSupport(bagian dari Rails) sejak v3.1, seperti yang ditunjukkan oleh @campaterson. Jadi di dalam Rails, atau jika Anda require 'active_support', Anda dapat menulis:

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

OTOH, tidak ada inoperator atau #in?metode dalam Ruby itu sendiri, meskipun telah diusulkan sebelumnya, khususnya oleh Yusuke Endoh seorang anggota terkemuka ruby-core.

Seperti yang ditunjukkan oleh orang lain, metode terbalik include?ada, untuk semua Enumerables termasuk Array, Hash, Set, Range:

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

Perhatikan bahwa jika Anda memiliki banyak nilai dalam array Anda, mereka semua akan diperiksa satu demi satu (yaitu O(n)), sedangkan pencarian untuk hash akan menjadi waktu yang konstan (yaitu O(1)). Jadi, jika array Anda konstan, misalnya, lebih baik menggunakan Set sebagai gantinya. Misalnya:

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

Sebuah tes cepat menunjukkan bahwa menelepon include?pada elemen 10 Setadalah sekitar 3.5x lebih cepat daripada menelepon pada setara Array(jika unsur ini tidak ditemukan).

Catatan penutup terakhir: berhati-hatilah saat menggunakan include?pada Range, ada kehalusan, jadi rujuk ke dokumen dan bandingkan dengan cover?...


2
Meskipun Ruby tidak termasuk #in?dalam inti itu, jika Anda menggunakan Rails, itu tersedia. api.rubyonrails.org/classes/Object.html#method-i-in-3F (Saya tahu ini adalah Ruby, bukan pertanyaan Rails, tetapi mungkin membantu siapa pun yang ingin menggunakan #in?Rails. Sepertinya itu ditambahkan di Rails. 3.1 apidock.com/rails/Object/in%3F
campeterson

166

Mencoba

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

ini sintaks yang lebih lama, lihat ^^^ @brian's answer
jahrichie

62
@jahrichie apa sebenarnya yang Anda anggap "sintaksis lama" dalam jawaban ini, tanda kurung opsional?
Dennis

3
saya setuju @ Dennis, ini bukan lebih tua, tanda kurung adalah opsional dan dalam sebagian besar kasus PRAKTEK YANG BAIK .... coba gunakan sertakan tanpa tanda kurung dalam satu baris jika kalimat misalnya, apa yang saya maksudkan adalah bahwa tergantung pada kasus Anda, Anda harus atau harus menggunakan tanda kurung atau tidak (sama sekali tidak terkait dengan sintaks ruby ​​"lama")
d1jhoni1b

Ini adalah satu-satunya sintaks yang bisa saya peroleh dalam operasi ternary.
Michael Cameron

49

Gunakan Enumerable#include:

a = %w/Cat Dog Bird/

a.include? 'Dog'

Atau, jika sejumlah tes dilakukan, 1 Anda dapat menyingkirkan loop (yang bahkan include?telah) dan beralih dari O (n) ke O (1) dengan:

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


1. Saya harap ini jelas tetapi untuk mencegah keberatan: ya, hanya untuk beberapa pencarian, Hash [] dan transpose ops mendominasi profil dan masing-masing O (n) sendiri.


49

Jika Anda ingin memeriksa dengan blok, Anda dapat mencoba any?atau 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  

Lihat Enumerable untuk informasi lebih lanjut.

Inspirasi saya berasal dari " mengevaluasi apakah array memiliki item dalam ruby "


1
Sangat berguna jika Anda ingin memeriksa salah satu / semua string tersebut termasuk dalam string lain / konstan
thanikkal

43

Ruby memiliki sebelas metode untuk menemukan elemen dalam array.

Yang disukai adalah include?atau, untuk akses berulang, membuat Set dan kemudian memanggil include?atau member?.

Inilah semuanya:

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

Mereka semua mengembalikan truenilai ish jika elemen ada.

include?adalah metode yang disukai. Ini menggunakan forloop bahasa-C secara internal yang rusak ketika elemen cocok dengan rb_equal_opt/rb_equalfungsi internal . Itu tidak bisa menjadi jauh lebih efisien kecuali Anda membuat Set untuk cek keanggotaan berulang.

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?tidak didefinisikan ulang di Arraykelas dan menggunakan implementasi yang tidak dioptimalkan dari Enumerablemodul yang secara harfiah menyebutkan semua elemen:

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

Diterjemahkan ke kode Ruby hal ini tentang hal berikut:

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

Keduanya include?dan member?memiliki kompleksitas waktu O (n) karena keduanya mencari array untuk kemunculan pertama dari nilai yang diharapkan.

Kita dapat menggunakan Set untuk mendapatkan O (1) waktu akses dengan biaya harus membuat representasi Hash dari array terlebih dahulu. Jika Anda berulang kali memeriksa keanggotaan pada larik yang sama, investasi awal ini dapat melunasi dengan cepat. Settidak diimplementasikan dalam C tetapi sebagai kelas Ruby biasa, masih waktu akses O (1) yang mendasarinya @hashmembuat ini berharga.

Berikut ini adalah implementasi dari kelas 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

Seperti yang Anda lihat kelas Set hanya membuat @hashinstance internal , memetakan semua objek ke truedan kemudian memeriksa keanggotaan menggunakan Hash#include?yang diimplementasikan dengan O (1) waktu akses di kelas Hash.

Saya tidak akan membahas tujuh metode lainnya karena semuanya kurang efisien.

Sebenarnya ada lebih banyak metode dengan kompleksitas O (n) di luar 11 yang tercantum di atas, tetapi saya memutuskan untuk tidak mencantumkannya karena mereka memindai seluruh array daripada merusak pada pertandingan pertama.

Jangan gunakan ini:

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

Bagaimana berani mengatakan bahwa Ruby memiliki 11 cara untuk melakukan apa saja! Tidak lebih cepat dari yang Anda katakan bahwa seseorang akan menunjukkan bahwa Anda melewatkan # 12, lalu # 13, dan seterusnya. Untuk menegaskan maksud saya, saya akan menyarankan cara-cara lain, tetapi pertama-tama izinkan saya mempertanyakan 11cara - cara yang telah Anda sebutkan. Pertama, Anda hampir tidak dapat menghitung indexdan find_index(atau finddan detect) sebagai metode yang terpisah, karena mereka hanya nama yang berbeda untuk metode yang sama. Kedua, semua ekspresi yang diakhiri dengan > 0salah, yang saya yakin adalah kekhilafan. (lanjutan)
Cary Swoveland

... arr.index(e), misalnya, mengembalikan 0jika arr[0] == e. Anda akan ingat arr.index(e)pengembalian niljika etidak ada. indextidak dapat digunakan, namun, jika seseorang mencari nildi arr. (Masalah yang sama dengan rindex, yang tidak terdaftar.). Mengubah array menjadi himpunan dan kemudian menggunakan metode himpunan adalah sedikit peregangan. Lalu mengapa tidak dikonversi ke hash (dengan kunci dari array dan nilai arbitrer), lalu gunakan metode hash? Bahkan jika mengonversi ke set tidak apa-apa, ada metode set lain yang bisa digunakan, seperti !arr.to_set.add?(e). (lanjutan)
Cary Swoveland

... Seperti yang dijanjikan, berikut adalah beberapa metode yang lebih yang dapat digunakan: arr.count(e) > 0, arr != arr.dup.delete(e) , arr != arr - [e]dan arr & [e] == [e]. Orang juga bisa menggunakan selectdan reject.
Cary Swoveland

Saya sangat merekomendasikan menggunakan Set jika kriteria adalah tidak ada duplikat diperbolehkan, dan mengetahui apakah ada elemen tertentu dalam daftar. Set adalah SOooo jauh lebih cepat. Array benar-benar tidak boleh digunakan saat pencarian diperlukan; Mereka lebih baik digunakan sebagai antrian di mana barang disimpan sementara untuk diproses secara berurutan. Hash dan Set lebih baik untuk melihat apakah ada sesuatu.
the Tin Man

30

Beberapa jawaban menyarankan Array#include?, tetapi ada satu peringatan penting: Melihat sumbernya, bahkan Array#include?melakukan perulangan:

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

Cara untuk menguji keberadaan kata tanpa perulangan adalah dengan membuat trie untuk array Anda. Ada banyak implementasi trie di luar sana (google "ruby trie"). Saya akan gunakan rambling-triedalam contoh ini:

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 }

Dan sekarang kami siap untuk menguji keberadaan berbagai kata dalam array Anda tanpa mengulanginya, pada O(log n)waktunya, dengan kesederhanaan sintaksis yang sama seperti Array#include?, menggunakan sublinear Trie#include?:

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

8
a.each do ... endUmm ... tidak yakin bagaimana itu bukan loop
tckmn

27
Perhatikan bahwa ini sebenarnya termasuk loop; apa pun yang bukan O (1) termasuk semacam loop. Kebetulan itu adalah loop di atas karakter dari string input. Juga perhatikan dari jawaban yang telah disebutkan Set#include?untuk orang-orang yang peduli tentang efisiensi; ditambah dengan menggunakan simbol bukan string, itu bisa menjadi O (1) kasus rata-rata (jika Anda menggunakan string, maka hanya menghitung hash adalah O (n) di mana n adalah panjang string). Atau jika Anda ingin menggunakan perpustakaan pihak ketiga, Anda bisa menggunakan hash sempurna yang merupakan O (1) kasus terburuk.
Brian Campbell

4
AFAIK, Setmenggunakan hash untuk mengindeks anggota-anggotanya, jadi sebenarnya Set#include? harus memiliki kompleksitas O (1) untuk terdistribusi dengan baik Set(lebih khusus O (ukuran input) untuk hashing, dan O (log (n / bucket-number)) untuk pencarian)
Uri Agassi

11
Biaya untuk menciptakan dan memelihara trie sama besarnya. Jika Anda melakukan banyak operasi pencarian pada array, maka memori dan biaya waktu untuk mengisi sebuah trie dan mempertahankannya sepadan, tetapi untuk cek tunggal, atau bahkan ratusan atau ribuan cek, O (n) sangat cocok. Opsi lain yang tidak memerlukan penambahan dependensi adalah mengurutkan array atau mempertahankannya dalam urutan yang terurut, dalam hal ini operasi pencarian biner O (lg n) dapat digunakan untuk memeriksa inklusi.
speakingcode

1
@speaking kode, Anda mungkin benar dari sudut pandang pragmatis. Tetapi OP meminta untuk "memeriksa apakah nilainya ada, tidak lebih, tanpa perulangan". Ketika saya menulis jawaban ini, ada banyak solusi pragmatis di sini, tetapi tidak ada yang benar-benar memenuhi persyaratan literal si penanya. Pengamatan Anda bahwa BST terkait dengan percobaan adalah benar, tetapi untuk string, trie adalah alat yang tepat untuk pekerjaan itu, bahkan Wikipedia tahu banyak . Kompleksitas membangun dan mempertahankan tri yang diimplementasikan dengan baik ternyata sangat menguntungkan.
Boris Stitnicky

18

Jika Anda tidak ingin mengulang, tidak ada cara untuk melakukannya dengan Array. Anda harus menggunakan Set sebagai gantinya.

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

Set berfungsi secara internal seperti Hash, sehingga Ruby tidak perlu mengulang-ulang koleksi untuk menemukan item, karena seperti namanya, ia menghasilkan hash tombol dan membuat peta memori sehingga setiap hash menunjuk ke titik tertentu dalam memori. Contoh sebelumnya dilakukan dengan Hash:

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

Kelemahannya adalah set dan kunci hash hanya dapat menyertakan item unik dan jika Anda menambahkan banyak item, Ruby harus mengulang semuanya setelah sejumlah item tertentu untuk membangun peta baru yang sesuai dengan ruang tombol yang lebih besar. Untuk lebih lanjut tentang ini, saya sarankan Anda menonton " MountainWest RubyConf 2014 - Big O in a Homemade Hash oleh Nathan Long ".

Inilah patokan:

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

Dan hasilnya:

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

Jika Anda menggunakan deteksi, maka Anda setidaknya dapat mengurangi perulangan. Deteksi akan berhenti pada item pertama 'terdeteksi' (blok yang dilewati untuk item bernilai true). Selain itu, Anda dapat memberi tahu mendeteksi apa yang harus dilakukan jika tidak ada yang terdeteksi (Anda dapat memasukkan lambda).
aenw

1
@ aenw tidak include?berhenti pada hit pertama?
Kimmo Lehto

Anda memang benar. Saya sangat terbiasa menggunakan mendeteksi bahwa saya lupa tentang memasukkan. terima kasih atas komentar Anda - itu memastikan bahwa saya menyegarkan pengetahuan saya.
aenw

include?berhenti pada hit pertama tetapi jika hit itu di akhir daftar .... Solusi apa pun yang bergantung pada Array untuk penyimpanan akan memiliki kinerja yang menurun ketika daftar tumbuh, terutama ketika harus menemukan elemen di akhir daftar. daftar. Hash dan Set tidak memiliki masalah itu, begitu pula daftar yang dipesan dan pencarian biner.
the Tin Man

Cukup banyak jawaban ini tentang jawaban pertama :)
Kimmo Lehto

17

Ini adalah cara lain untuk melakukan ini: gunakan Array#indexmetode ini.

Ini mengembalikan indeks kemunculan pertama elemen dalam array.

Sebagai contoh:

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

index() juga dapat mengambil blok:

Sebagai contoh:

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

Ini mengembalikan indeks kata pertama dalam array yang berisi huruf 'o'.


indexmasih mengulangi array, itu hanya mengembalikan nilai elemen.
the Tin Man

13

Fakta yang menyenangkan,

Anda dapat menggunakan *untuk memeriksa keanggotaan array dalam caseekspresi.

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

Perhatikan sedikit *dalam klausa when, ini memeriksa keanggotaan dalam array.

Semua perilaku sulap biasa dari operator percikan berlaku, jadi misalnya jika arraysebenarnya bukan array tetapi satu elemen akan cocok dengan elemen itu.


Senang mendengarnya..!
Cary Swoveland

Ini juga akan menjadi pemeriksaan pertama yang lambat dalam pernyataan kasus, jadi saya akan menggunakannya dalam whenkemungkinan terakhir sehingga yang lain, cek yang lebih cepat, dikeluarkan dengan cepat.
the Tin Man

9

Ada banyak cara untuk mencapai ini. Beberapa di antaranya adalah sebagai berikut:

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

2.in? a  #=> true

8.in? a #=> false

a.member? 1 #=> true

a.member? 8 #=> false

6
Catatan yang Object#in?hanya ditambahkan ke Rails (yaitu ActiveSupport) v3.1 +. Ini tidak tersedia di Ruby inti.
Tom Lord

5

Jika Anda perlu memeriksa beberapa kali untuk kunci apa saja, konversikan arrke hash, dan sekarang periksa di 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

Kinerja Hash # has_key? versus Array # include?

Parameter Hash # has_key? Array # termasuk

Kompleksitas Waktu O (1) operasi O (n) operasi 

Jenis Akses Mengakses Hash [kunci] jika iterasi melalui setiap elemen
                        mengembalikan nilai apa pun dari array hingga itu
                        true dikembalikan ke menemukan nilai dalam Array
                        Hash # has_key? panggilan
                        panggilan    

Untuk sekali pakai cek include?baik-baik saja


Apakah Anda masih belum melalui array untuk mengubahnya menjadi hash?
chrisjacob

2
ya, saya lakukan tetapi sekarang saya memiliki jawaban yang sudah dihitung sebelumnya untuk memeriksa apakah ada elemen dalam array atau tidak. Sekarang, waktu berikutnya ketika saya mencari kata lain, itu akan membutuhkan O (1) untuk kueri, benar meskipun pra-perhitungan akan mengambil O (n). Sebenarnya saya sudah menggunakan include? dalam aplikasi saya di dalam loop untuk memeriksa apakah elemen tertentu ada dalam array atau tidak, itu merusak kinerja, itu diperiksa di ruby-prof, itu adalah hambatan
aqfaridi

1
.... tetapi O (n) ruang dan juga, bukan saatnya O (n), karena seperti yang ditunjukkan oleh @ chris72205, Anda harus beralih melalui array Anda terlebih dahulu. O (1) + O (n) = O (n). Jadi sebenarnya, ini jauh lebih buruk daripada memasukkan?
Rambatino

Bung saya hanya mengatakan jangan gunakan termasuk di dalam lingkaran, untuk memeriksa waktu menggunakan menggunakan baik-baik saja. Jadi tolong baca dulu use case, lebih baik jika Anda perlu memeriksa beberapa kali di dalam loop.
aqfaridi

Mengubah array ke hash adalah mahal, tetapi menggunakan array untuk mencari adalah struktur yang salah. Array lebih baik sebagai antrian di mana pesanan mungkin penting; Hash dan Set lebih baik ketika inklusi / keberadaan / keunikan itu penting. Mulai dengan wadah yang tepat dan masalahnya diperdebatkan.
the Tin Man

4

Ini akan memberi tahu Anda tidak hanya bahwa itu ada tetapi juga berapa kali muncul:

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

12
Tidak ada gunanya menggunakan ini kecuali jika Anda ingin tahu berapa kali muncul, karena .any?akan kembali segera setelah menemukan elemen yang cocok pertama, .countakan selalu memproses seluruh array.
Zaz

Meskipun ini secara teknis akan mengetahui apakah ada sesuatu, itu juga BUKAN cara yang tepat untuk melakukannya jika Anda peduli dengan kecepatan.
the Tin Man

4

Untuk apa nilainya, The Ruby docs adalah sumber yang luar biasa untuk pertanyaan-pertanyaan semacam ini.

Saya juga akan mencatat panjang array yang Anda cari. The include?Metode akan menjalankan pencarian linear dengan O (n) kompleksitas yang bisa mendapatkan cukup jelek tergantung pada ukuran array.

Jika Anda bekerja dengan array besar (diurutkan), saya akan mempertimbangkan untuk menulis algoritma pencarian biner yang seharusnya tidak terlalu sulit dan memiliki kasus terburuk O (log n).

Atau jika Anda menggunakan Ruby 2.0, Anda dapat memanfaatkan bsearch.


3
Pencarian biner mengasumsikan array diurutkan (atau dipesan dalam beberapa bentuk) yang dapat mahal untuk array besar, sering meniadakan keuntungan.
the Tin Man

Pencarian biner juga mengharuskan semua pasangan elemen untuk dapat dibandingkan <=>, yang tidak selalu demikian. Misalkan, misalnya, elemen array adalah hash.
Cary Swoveland

1
@CarySwoveland seharusnya kurang lebih menyiratkan bahwa elemen dalam array yang diurutkan sebanding.
davissp14

4

Anda dapat mencoba:

Contoh: jika Kucing dan Anjing ada dalam array:

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

Dari pada:

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

Catatan: member?dan include?sama.

Ini dapat melakukan pekerjaan dalam satu baris!


3

Jika kita ingin tidak menggunakan include?ini juga berfungsi:

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

3
apa saja? juga menerima blok: ['kucing', 'anjing', 'kuda']. {| 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. Apakah nilditemukan?
Cary Swoveland

2

Bagaimana dengan ini?

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

Itu masih akan beralih ke array hanya untuk menemukan elemen. Maka harus mengembalikan indeks elemen itu.
Manusia Timah

2

Jika Anda mencoba melakukan ini dalam tes unit MiniTest , Anda dapat menggunakannya assert_includes. Contoh:

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

2

Ada cara lain untuk ini.

Misalkan susunannya adalah [ :edit, :update, :create, :show ], yah mungkin keseluruhan tujuh dosa yang mematikan / tenang .

Dan mainan lebih lanjut dengan ide menarik tindakan yang valid dari beberapa string:

"my brother would like me to update his profile"

Kemudian:

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

1
Regex Anda /[,|.| |]#{v.to_s}[,|.| |]/,, membuat saya berpikir Anda ingin menemukan 'nama tindakan yang dikelilingi oleh salah satu dari: koma, titik, ruang, atau tidak sama sekali', tetapi ada beberapa bug halus. "|update|"akan kembali [:update]dan "update"akan kembali []. Kelas karakter ( [...]) tidak menggunakan pipa ( |) untuk memisahkan karakter. Meskipun kami mengubahnya menjadi grup ( (...)), Anda tidak dapat mencocokkan karakter kosong. Jadi regex yang mungkin Anda inginkan adalah/(,|\.| |^)#{v.to_s}(,|\.| |$)/
bkDJ

regexnya ok (centang rubular.com) - dan @Rambatino: kenapa akan seperti Amazon Echo misalnya;) Anda bisa mengatakan: "tolong tambahkan ayam ke daftar belanjaan saya" (dan - yah - maka Anda harus tambahkan: tambahkan ke array, tapi saya rasa Anda mendapatkan intinya;)
walt_die

1
"regex ok (diperiksa rubular.com)". Itu tidak baik. Regex Anda tidak akan cocok dengan kata kunci di awal atau akhir string (mis. "Perbarui profil saudara saya"). Jika Anda tidak ingin mencocokkan awal atau akhir, maka regex Anda masih tidak ok karena kelas karakter di kedua sisi kata kunci seharusnya/[,. ]/
bkDJ

Seperti @bkDJ katakan, regex salah. rubular.com/r/4EG04rANz6KET6
the Tin Man

1

Jika Anda ingin mengembalikan nilai bukan hanya benar atau salah, gunakan

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

Ini akan mengembalikan 'Anjing' jika ada dalam daftar, jika tidak nihil.


Atau penggunaan array.any?{|x| x == 'Dog'}jika Anda tidak ingin benar / salah (tidak nilai), tetapi juga ingin membandingkan terhadap blok seperti ini.
mahemoff

0

Berikut ini satu cara lagi untuk melakukan ini:

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

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

1
Ini adalah cara yang sangat tidak efisien untuk melakukan ini! Saya tidak downvoting karena secara teknis tidak salah, dan seseorang mungkin belajar sesuatu tentang Ruby dari membacanya, tetapi ada banyak jawaban yang lebih baik di atas.
jibberia

Conehead, Anda dapat menyederhanakannya arr != arr - [e]. arr & [e] == [e]adalah cara lain di sepanjang garis yang sama.
Cary Swoveland

@CarySwoveland Jangan mengolok-olok topi penyihir ;-)
Wand Maker

Saya mengacu pada kepala penyihir, bukan topi, dengan rasa hormat terbesar.
Cary Swoveland


0

ia memiliki banyak cara untuk menemukan elemen dalam array apa pun tetapi cara paling sederhana adalah 'di?' metode.

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

2
Catatan: Seperti dijelaskan dalam jawaban ini , metode in?membutuhkan ActiveSupportyang akan diimpor: require active_support.
Patrick

Tidak memerlukan semua ActiveSupport jika Anda menggunakan ekstensi inti .
the Tin Man

0

jika Anda tidak ingin menggunakan, include?Anda pertama-tama dapat membungkus elemen dalam array dan kemudian memeriksa apakah elemen yang dibungkus sama dengan persimpangan array dan elemen yang dibungkus. Ini akan mengembalikan nilai boolean berdasarkan kesetaraan.

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

-1

Saya selalu merasa tertarik untuk menjalankan beberapa tolok ukur untuk melihat kecepatan relatif dari berbagai cara melakukan sesuatu.

Menemukan elemen array di awal, tengah atau akhir akan memengaruhi pencarian linear apa pun, tetapi hampir tidak memengaruhi pencarian terhadap Set.

Mengkonversi Array ke Set akan menyebabkan hit dalam waktu pemrosesan, jadi buat Set dari Array sekali, atau mulai dengan Set dari awal.

Inilah kode patokan:

# 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

Yang mana, ketika dijalankan di laptop Mac OS saya, menghasilkan:

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

Pada dasarnya hasilnya memberitahu saya untuk menggunakan Set untuk semuanya jika saya akan mencari inklusi kecuali saya dapat menjamin bahwa elemen pertama adalah yang saya inginkan, yang sangat tidak mungkin. Ada beberapa overhead ketika memasukkan elemen ke dalam hash, tetapi waktu pencarian jauh lebih cepat, saya tidak berpikir itu harus menjadi pertimbangan. Sekali lagi, jika Anda perlu mencarinya, jangan gunakan Array, gunakan Set. (Atau Hash.)

Semakin kecil Array, semakin cepat metode Array akan berjalan, tetapi mereka masih tidak akan mengikuti, meskipun dalam array kecil perbedaannya mungkin kecil.

"Pertama", "Tengah" dan "Terakhir" mencerminkan penggunaan first, size / 2dan lastuntuk ARRAYuntuk elemen yang dicari. Elemen itu akan digunakan saat mencari ARRAYdan SETvariabel.

Perubahan kecil dibuat untuk metode yang membandingkan > 0karena tes harus >= 0untuk indextes tipe.

Informasi lebih lanjut tentang Fruity dan metodologinya tersedia di README -nya .

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.