ฉันมีค่า'Dog'
และอาเร['Cat', 'Dog', 'Bird']
ย์
ฉันจะตรวจสอบว่ามีอยู่ในอาร์เรย์โดยไม่วนซ้ำได้อย่างไร มีวิธีง่าย ๆ ในการตรวจสอบว่ามีค่าอยู่หรือไม่
ฉันมีค่า'Dog'
และอาเร['Cat', 'Dog', 'Bird']
ย์
ฉันจะตรวจสอบว่ามีอยู่ในอาร์เรย์โดยไม่วนซ้ำได้อย่างไร มีวิธีง่าย ๆ ในการตรวจสอบว่ามีค่าอยู่หรือไม่
คำตอบ:
คุณกำลังมองหาinclude?
:
>> ['Cat', 'Dog', 'Bird'].include? 'Dog'
=> true
%w(Cat Dog Bird).include? 'Dog'
#include?
ยังคงทำการวนซ้ำ แม้ว่า coder จะถูกบันทึกจากการเขียนลูปอย่างชัดเจน ฉันได้เพิ่มคำตอบที่ทำงานอย่างแท้จริงโดยไม่ต้องวนลูป
มีin?
วิธีการในActiveSupport
(ส่วนหนึ่งของ Rails) ตั้งแต่ v3.1 ตามที่ระบุโดย @campaterson ดังนั้นภายใน Rails หรือถ้าคุณrequire 'active_support'
คุณสามารถเขียน:
'Unicorn'.in?(['Cat', 'Dog', 'Bird']) # => false
OTOH ไม่มีin
โอเปอเรเตอร์หรือ#in?
วิธีการในตัวของ Ruby แม้ว่าจะได้รับการเสนอมาก่อนโดยเฉพาะอย่างยิ่งโดย Yusuke Endohสมาชิกระดับแนวหน้าของ ruby-core
เป็นแหลมออกโดยคนอื่น ๆ วิธีการย้อนกลับinclude?
ที่มีอยู่สำหรับทุกEnumerable
ท่านรวมทั้งArray
, Hash
, Set
, Range
:
['Cat', 'Dog', 'Bird'].include?('Unicorn') # => false
โปรดทราบว่าหากคุณมีค่ามากมายในอาเรย์ของคุณค่าเหล่านั้นจะถูกตรวจสอบหนึ่งค่า (เช่นO(n)
) ในขณะที่การค้นหาแฮชนั้นจะเป็นเวลาคงที่ (เช่นO(1)
) ดังนั้นหากคุณมีค่าคงที่เช่นคุณควรใช้ชุดแทน เช่น:
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?
กับ a Range
, มีรายละเอียดย่อยดังนั้นให้อ้างอิงเอกสารและเปรียบเทียบกับcover?
...
#in?
อยู่ในแกน แต่ถ้าคุณใช้ Rails ก็สามารถใช้งานได้ api.rubyonrails.org/classes/Object.html#method-i-in-3F (ฉันรู้ว่านี่คือ Ruby ไม่ใช่คำถาม Rails แต่อาจช่วยให้ทุกคนที่ต้องการใช้งาน#in?
ใน Rails ดูเหมือนว่าจะถูกเพิ่มเข้าไปใน Rails 3.1 apidock.com/rails/Object/in%3F
ลอง
['Cat', 'Dog', 'Bird'].include?('Dog')
ใช้Enumerable#include
:
a = %w/Cat Dog Bird/
a.include? 'Dog'
หรือถ้าทำการทดสอบเป็นจำนวนมาก1คุณสามารถกำจัดลูป (ที่include?
มี) และเปลี่ยนจากO (n)ถึงO (1)ด้วย:
h = Hash[[a, a].transpose]
h['Dog']
หากคุณต้องการที่จะตรวจสอบได้โดยบล็อกคุณอาจจะลองหรือ 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สำหรับข้อมูลเพิ่มเติม
แรงบันดาลใจของฉันมาจาก " ประเมินว่าอาร์เรย์มีรายการใดในทับทิม "
Ruby มีสิบเอ็ดวิธีในการค้นหาองค์ประกอบในอาร์เรย์
ที่ต้องการหนึ่ง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) เวลาเข้าถึงโดยที่ไม่ต้องสร้าง Hash ที่เป็นตัวแทนของอาร์เรย์ก่อน หากคุณตรวจสอบสมาชิกภาพซ้ำหลายครั้งในอาร์เรย์เดียวกันการลงทุนครั้งแรกสามารถชำระได้อย่างรวดเร็ว Set
ไม่ได้ดำเนินการใน C แต่เป็นคลาส Ruby ธรรมดายังคงเวลาเข้าถึง O (1) ของต้นแบบ@hash
ทำให้คุ้มค่า
นี่คือการใช้งานของ Set class:
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
ในขณะที่คุณสามารถเห็นการตั้งค่าคลาสเพิ่งสร้าง@hash
อินสแตนซ์ภายในแมปวัตถุทั้งหมดไปtrue
แล้วตรวจสอบการเป็นสมาชิกHash#include?
ที่ใช้กับ O (1) เวลาเข้าถึงในคลาสแฮช
ฉันจะไม่พูดถึงเจ็ดวิธีอื่น ๆ เพราะมันมีประสิทธิภาพน้อยกว่า
จริงๆแล้วมีวิธีการมากขึ้นที่มีความซับซ้อน O (n) เกินกว่า 11 รายการข้างต้น แต่ฉันตัดสินใจที่จะไม่แสดงรายการพวกเขาเนื่องจากพวกเขาสแกนอาร์เรย์ทั้งหมดแทนที่จะทำลายในการแข่งขันครั้งแรก
อย่าใช้สิ่งเหล่านี้:
# bad examples
array.grep(element).any?
array.select { |each| each == element }.size > 0
...
11
วิธีที่คุณระบุ ก่อนอื่นคุณแทบจะไม่สามารถนับindex
และfind_index
(หรือfind
และdetect
) เป็นวิธีแยกต่างหากเนื่องจากเป็นชื่อที่แตกต่างกันสำหรับวิธีการเดียวกัน ประการที่สองการแสดงออกทั้งหมดที่ลงท้ายด้วย> 0
ไม่ถูกต้องซึ่งฉันแน่ใจว่าเป็นการกำกับ (ต่อ)
arr.index(e)
ยกตัวอย่างเช่นส่งกลับถ้า0
arr[0] == e
คุณจะเรียกคืนarr.index(e)
ผลตอบแทนnil
ถ้าe
ไม่เป็นปัจจุบัน index
ไม่สามารถนำมาใช้ แต่หากมีการค้นหาในnil
arr
(ปัญหาเดียวกันกับrindex
ที่ไม่ได้อยู่ในรายการ) การแปลงอาเรย์เป็นเซ็ตจากนั้นใช้เมธอด set เป็นบิตของการยืด ทำไมจึงไม่แปลงเป็นแฮช (ด้วยคีย์จากอาร์เรย์และค่าที่กำหนดเอง) จากนั้นใช้เมธอดแฮช แม้ว่าการแปลงไปเป็นชุดเป็น OK มีวิธีการตั้งค่าอื่น ๆ !arr.to_set.add?(e)
ที่สามารถนำมาใช้เช่น (ต่อ)
arr.count(e) > 0
, arr != arr.dup.delete(e)
, และarr != arr - [e]
arr & [e] == [e]
หนึ่งยังสามารถจ้างและselect
reject
คำตอบหลายข้อเสนอแนะ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สำหรับอาร์เรย์ของคุณ มีการใช้งาน 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 }
และตอนนี้เราก็พร้อมที่จะทดสอบการมีอยู่ของคำศัพท์ต่าง ๆ ในอาร์เรย์ของคุณโดยไม่ต้องวนซ้ำในO(log n)
เวลานั้นด้วยความเรียบง่ายทางวากยสัมพันธ์เช่นเดียวกับการArray#include?
ใช้ซับลิเนียร์Trie#include?
:
trie.include? 'bird' #=> true
trie.include? 'duck' #=> false
a.each do ... end
อืมม ... ไม่แน่ใจว่ามันไม่ใช่ลูปอย่างไร
Set#include?
สำหรับผู้ที่กังวลเกี่ยวกับประสิทธิภาพ ควบคู่ไปกับการใช้สัญลักษณ์แทนสตริงมันสามารถเป็นกรณี O (1) โดยเฉลี่ย (ถ้าคุณใช้สตริงจากนั้นเพียงแค่คำนวณแฮชคือ O (n) โดยที่ n คือความยาวของสตริง) หรือถ้าคุณต้องการใช้ห้องสมุดบุคคลที่สามคุณสามารถใช้แฮชที่สมบูรณ์แบบซึ่งเป็นกรณี O ที่เลวร้ายที่สุด
Set
ใช้แฮชเพื่อจัดทำดัชนีสมาชิกดังนั้นจริงๆแล้วSet#include?
ควรมีความซับซ้อน O (1) สำหรับการกระจายที่ดีSet
(โดยเฉพาะ O (ขนาดอินพุต) สำหรับการแฮชและ O (log (n / bucket-number)) สำหรับ การค้นหา)
หากคุณไม่ต้องการวนซ้ำคุณไม่สามารถทำได้ด้วย Arrays คุณควรใช้ชุดแทน
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
ข้อเสียคือปุ่มชุดและแฮชสามารถรวมเฉพาะรายการที่ไม่ซ้ำกันและถ้าคุณเพิ่มรายการจำนวนมากทับทิมจะต้องทำสิ่งใหม่ทั้งหมดหลังจากรายการจำนวนหนึ่งเพื่อสร้างแผนที่ใหม่ที่เหมาะกับพื้นที่คีย์ขนาดใหญ่ สำหรับข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ฉันขอแนะนำให้คุณดู " MountainWest RubyConf 2014 - Big O ในแฮ็คโฮมเมดโดย 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)
include?
หยุดตีแรก?
include?
หยุดที่การโจมตีครั้งแรก แต่ถ้าการเข้าตีนั้นอยู่ในตอนท้ายของรายการ .... โซลูชันใด ๆ ที่ใช้ Array สำหรับการจัดเก็บจะมีประสิทธิภาพลดลงเมื่อรายการเติบโตโดยเฉพาะอย่างยิ่งเมื่อต้องค้นหาองค์ประกอบที่ส่วนท้ายของ รายการ. Hash and Set ไม่มีปัญหานั้นและจะไม่มีรายการสั่งซื้อและการค้นหาแบบไบนารี
นี่เป็นอีกวิธีหนึ่งในการทำสิ่งนี้: ใช้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
ยังคงวนซ้ำอาร์เรย์ แต่จะคืนค่าขององค์ประกอบ
สนุกกับความเป็นจริง,
คุณสามารถใช้*
เพื่อตรวจสอบการเป็นสมาชิกอาร์เรย์ในcase
นิพจน์
case element
when *array
...
else
...
end
สังเกตุสิ่งเล็ก ๆ น้อย ๆ*
ในอนุประโยคเมื่อตรวจสอบนี้เป็นสมาชิกในอาร์เรย์
พฤติกรรมเวทย์มนตร์ปกติของตัวดำเนินการ splat จะมีผลบังคับใช้ตัวอย่างเช่นถ้าarray
ไม่ใช่อาร์เรย์จริง ๆ แต่มีองค์ประกอบเดียวที่จะจับคู่กับองค์ประกอบนั้น
when
เป็นไปได้ดังนั้นการตรวจสอบอื่น ๆ ที่เร็วกว่าและกำจัดวัชพืชออกอย่างรวดเร็ว
มีหลายวิธีในการทำสิ่งนี้ให้สำเร็จ บางส่วนของพวกเขามีดังนี้:
a = [1,2,3,4,5]
2.in? a #=> true
8.in? a #=> false
a.member? 1 #=> true
a.member? 8 #=> false
Object#in?
เพิ่มเฉพาะ Rails (เช่นActiveSupport
) v3.1 + มันไม่สามารถใช้ได้ใน core Ruby
หากคุณต้องการตรวจสอบหลายครั้งสำหรับคีย์ใด ๆ แปลงarr
เป็นhash
และตอนนี้ตรวจสอบใน 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 # รวมถึง?
พารามิเตอร์แฮช # has_key? อาร์เรย์ # ได้แก่ ความซับซ้อนของเวลาการดำเนินงาน O (1) O (n) ประเภทการเข้าถึงการเข้าถึง Hash [แป้น] ถ้ามันซ้ำผ่านแต่ละองค์ประกอบ ส่งคืนค่าใด ๆ จากนั้นอาร์เรย์จนกว่าจะได้รับ true ถูกส่งคืนไปยังการค้นหาค่าใน Array แฮ # has_key? โทร โทร
สำหรับการตรวจสอบครั้งเดียวใช้งานinclude?
ได้ดี
สิ่งนี้จะบอกคุณไม่เพียงว่ามันมีอยู่จริง แต่ยังปรากฏว่ามีกี่ครั้ง:
a = ['Cat', 'Dog', 'Bird']
a.count("Dog")
#=> 1
.any?
จะกลับมาทันทีที่พบองค์ประกอบแรกที่ตรงกัน.count
จะประมวลผลอาร์เรย์ทั้งหมดเสมอ
สำหรับสิ่งที่คุ้มค่าเอกสารของ Rubyเป็นแหล่งข้อมูลที่น่าอัศจรรย์สำหรับคำถามประเภทนี้
ฉันจะคำนึงถึงความยาวของอาเรย์ที่คุณกำลังค้นหาด้วย include?
วิธีการจะเรียกใช้การค้นหาเชิงเส้นที่มี O (n) ความซับซ้อนซึ่งสามารถได้รับน่าเกลียดสวยขึ้นอยู่กับขนาดของอาร์เรย์
หากคุณกำลังทำงานกับอาร์เรย์ขนาดใหญ่ (เรียงลำดับ) ฉันจะลองเขียนอัลกอริทึมการค้นหาแบบไบนารีซึ่งไม่ควรยากเกินไปและมีกรณีที่แย่ที่สุดของ O (log n)
หรือถ้าคุณกำลังใช้ทับทิม 2.0 bsearch
คุณสามารถใช้ประโยชน์จาก
<=>
ซึ่งไม่ได้เป็นทุกกรณี ตัวอย่างเช่นสมมติว่าองค์ประกอบของอาร์เรย์มีแฮช
คุณสามารถลอง:
ตัวอย่าง: ถ้ามี 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?
เหมือนกัน
สามารถทำงานได้ในหนึ่งบรรทัด!
ถ้าเราไม่ต้องการใช้include?
สิ่งนี้ก็ใช้งานได้:
['cat','dog','horse'].select{ |x| x == 'dog' }.any?
['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
พบ?
ด้วยวิธีนี้
['Cat', 'Dog', 'Bird'].index('Dog')
ถ้าคุณกำลังพยายามที่จะทำเช่นนี้ในMiniTestassert_includes
ทดสอบหน่วยคุณสามารถใช้ ตัวอย่าง:
pets = ['Cat', 'Dog', 'Bird']
assert_includes(pets, 'Dog') # -> passes
assert_includes(pets, 'Zebra') # -> fails
มีวิธีอื่น ๆ รอบนี้
สมมติว่าอาร์เรย์เป็น[ :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}[,|.| |]/}
/[,|.| |]#{v.to_s}[,|.| |]/
ทำให้ฉันคิดว่าคุณต้องการค้นหา 'ชื่อของการกระทำที่ล้อมรอบด้วยหนึ่งใน: จุลภาคระยะเวลาช่องว่างหรืออะไรเลย' แต่มีข้อบกพร่องเล็กน้อย "|update|"
จะกลับมา[:update]
และจะกลับมา"update"
[]
คลาสอักขระ ( [...]
) ไม่ใช้ไพพ์ (|
) เพื่อแยกอักขระ แม้ว่าเราจะเปลี่ยนเป็นกลุ่ม ( (...)
) คุณไม่สามารถจับคู่อักขระว่างเปล่าได้ ดังนั้น regex ที่คุณอาจต้องการคือ/(,|\.| |^)#{v.to_s}(,|\.| |$)/
/[,. ]/
หากคุณต้องการส่งคืนค่าไม่ใช่แค่จริงหรือเท็จใช้
array.find{|x| x == 'Dog'}
นี่จะส่งคืน 'สุนัข' หากมีอยู่ในรายการมิฉะนั้นจะไม่มี
array.any?{|x| x == 'Dog'}
ถ้าคุณไม่ต้องการ / จริงเท็จ (ไม่ได้ค่า) แต่ยังต้องการที่จะเปรียบเทียบกับบล็อกเช่นนี้
นี่เป็นอีกวิธีหนึ่งในการทำสิ่งนี้:
arr = ['Cat', 'Dog', 'Bird']
e = 'Dog'
present = arr.size != (arr - [e]).size
arr != arr - [e]
คุณสามารถลดความซับซ้อนของการ arr & [e] == [e]
เป็นอีกวิธีในแนวเดียวกัน
array = [ 'Cat', 'Dog', 'Bird' ]
array.include?("Dog")
มีหลายวิธีในการค้นหาองค์ประกอบในอาร์เรย์ใด ๆ แต่วิธีที่ง่ายที่สุดคือ 'ใน' วิธี.
example:
arr = [1,2,3,4]
number = 1
puts "yes #{number} is present in arr" if number.in? arr
หากคุณไม่ต้องการใช้include?
คุณสามารถห่อองค์ประกอบในอาร์เรย์ก่อนจากนั้นตรวจสอบว่าองค์ประกอบที่ถูกห่อนั้นเท่ากับจุดตัดของอาร์เรย์และองค์ประกอบที่ห่อหรือไม่ นี่จะคืนค่าบูลีนตามความเสมอภาค
def in_array?(array, item)
item = [item] unless item.is_a?(Array)
item == array & item
end
ฉันมักจะพบว่ามันน่าสนใจที่จะใช้การวัดประสิทธิภาพเพื่อดูความเร็วสัมพัทธ์ของวิธีต่างๆในการทำบางสิ่งบางอย่าง
การค้นหาองค์ประกอบอาเรย์ที่เริ่มต้นกลางหรือท้ายจะส่งผลต่อการค้นหาเชิงเส้นใด ๆ แต่แทบจะไม่ส่งผลกระทบต่อการค้นหากับชุด
การแปลง Array เป็น Set จะทำให้เวลาในการประมวลผลเป็นผลดังนั้นสร้าง Set จาก Array หนึ่งครั้งหรือเริ่มด้วย Set ตั้งแต่เริ่มต้น
นี่คือรหัสมาตรฐาน:
# 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 (หรือแฮช)
อาเรย์ที่เล็กกว่าจะเร็วกว่าวิธีอาเรย์ แต่ก็ยังคงไม่ทันตั้งตัว แต่ในอาเรย์เล็ก ๆ ความแตกต่างอาจจะเล็กน้อย
"ครั้งแรก", "กลาง" และ "สุดท้าย" สะท้อนให้เห็นถึงการใช้first
, size / 2
และlast
สำหรับARRAY
สำหรับเป็นองค์ประกอบค้นหา องค์ประกอบนั้นจะถูกใช้เมื่อค้นหาARRAY
และSET
ตัวแปร
มีการเปลี่ยนแปลงเล็กน้อยสำหรับวิธีการที่เปรียบเทียบกับ> 0
เพราะการทดสอบควรใช้>= 0
สำหรับindex
การทดสอบประเภท
ข้อมูลเพิ่มเติมเกี่ยวกับฟรุ๊ตตี้และวิธีการที่สามารถใช้ได้ในของREADME