Array รวมถึงค่าใด ๆ จากอาเรย์อื่น?


155

วิธีที่มีประสิทธิภาพมากที่สุดในการทดสอบว่าอาร์เรย์มีองค์ประกอบใด ๆ จากอาร์เรย์ที่สองหรือไม่

สองตัวอย่างด้านล่างการพยายามตอบคำถามfoodsประกอบด้วยองค์ประกอบใด ๆ จากcheeses:

cheeses = %w(chedder stilton brie mozzarella feta haloumi reblochon)
foods = %w(pizza feta foods bread biscuits yoghurt bacon)

puts cheeses.collect{|c| foods.include?(c)}.include?(true)

puts (cheeses - foods).size < cheeses.size

คำตอบ:


268
(cheeses & foods).empty?

ดังที่ Marc-André Lafortune กล่าวในความคิดเห็น&ทำงานในเวลาเชิงเส้นขณะที่any?+ include?จะเป็นกำลังสอง สำหรับชุดข้อมูลขนาดใหญ่เวลาเชิงเส้นจะเร็วขึ้น สำหรับชุดข้อมูลขนาดเล็กany?+ include?อาจเร็วกว่าที่คำตอบของ Lee Jarvis แสดง - อาจเป็นเพราะ&จัดสรร Array ใหม่ในขณะที่วิธีการแก้ปัญหาอื่นไม่ทำงาน


3
เมื่อตรวจสอบว่าอาเรย์มีองค์ประกอบจากอาเรย์อื่นมันจะไม่เหมาะสมกว่าที่จะทำ (ชีสและอาหาร) ใช่ไหม? เช่นนี้จะส่งกลับค่าที่แท้จริงถ้าในความเป็นจริงอาร์เรย์มีองค์ประกอบใด ๆ ที่เหมือนกันหรือไม่
Ryan Francis

1
@RyanFrancis เอกสาร: any?: วิธีการที่ผลตอบแทนจริงถ้าบล็อกที่เคยส่งกลับค่าอื่น ๆ นอกเหนือจากที่เป็นเท็จหรือศูนย์ empty?: คืนค่าจริงถ้าตัวเองไม่มีองค์ประกอบ
Nakilon

3
@Nakilon ฉันยังงงว่าทำไมคำตอบไม่ใช่(cheeses & foods).any?คำถามของ OP: ถ้ามีอาหารอะไรในชีส? ในตัวอย่างของเขา "feta" มีทั้งคู่ดังนั้นผลลัพธ์ควรเป็นจริงใช่ไหม ทำไมต้องตรวจสอบ.empty?สี่แยก
SuckerForMayhem

@SuckerForMayhem เนื่องจากคำถามของ OP คือ "ถ้ามี ... ?" ไม่ใช่แค่ "ถ้ามี" หากละเว้น" เป็น ... " จะถือว่าเป็น "หากมีจริงหรือไม่ " และจะส่งกลับค่าเท็จสำหรับอาเรย์เช่น[false, false, false]ในขณะที่เห็นได้ชัดว่าไม่ว่างเปล่า
Nakilon

มีการใช้งานในระดับ activerecord หรือไม่?
Lee Chun Hoe

35

วิธีการเกี่ยวกับEnumerable # ใด ๆ

>> cheeses = %w(chedder stilton brie mozzarella feta haloumi)
=> ["chedder", "stilton", "brie", "mozzarella", "feta", "haloumi"]
>> foods = %w(pizza feta foods bread biscuits yoghurt bacon)
=> ["pizza", "feta", "foods", "bread", "biscuits", "yoghurt", "bacon"]
>> foods.any? {|food| cheeses.include?(food) }
=> true

สคริปต์มาตรฐาน:

require "benchmark"
N = 1_000_000
puts "ruby version: #{RUBY_VERSION}"

CHEESES = %w(chedder stilton brie mozzarella feta haloumi).freeze
FOODS = %w(pizza feta foods bread biscuits yoghurt bacon).freeze

Benchmark.bm(15) do |b|
  b.report("&, empty?") { N.times { (FOODS & CHEESES).empty? } }
  b.report("any?, include?") { N.times { FOODS.any? {|food| CHEESES.include?(food) } } }
end

ผลลัพธ์:

ruby version: 2.1.9
                      user     system      total        real
&, empty?         1.170000   0.000000   1.170000 (  1.172507)
any?, include?    0.660000   0.000000   0.660000 (  0.666015)

คุณสามารถปรับปรุงสิ่งนี้ได้โดยเปลี่ยนcheesesเป็นชุด
akuhn

1
ใช้เกณฑ์มาตรฐานของฉันเองที่นี่ในทับทิม 2.2.7 และ 2.3.4 และany?, include?เร็วที่สุดตั้งค่าให้ช้าที่สุด: gist.github.com/jaredmoody/d2a1e83de2f91fd6865920cd01a8b497
Jared

4
การวัดประสิทธิภาพนี้มีอคติโดยตัวอย่างเฉพาะที่กล่าวถึงและไม่จำเป็นต้องถือในกรณีทั่วไปมากขึ้น เกิดอะไรขึ้นถ้าไม่มีองค์ประกอบทั่วไประหว่างสองอาร์เรย์? เกิดอะไรขึ้นถ้าอาร์เรย์มีลำดับที่แตกต่างกันในแต่ละรอบ เกิดอะไรขึ้นถ้า feta ปรากฏในตอนท้ายของอาร์เรย์ทั้งสอง ดังที่ Marc-Andréกล่าวว่าการแยกตั้งดำเนินการในเวลาเชิงเส้นดังนั้นจึงเหมาะสมที่จะปรับขนาดได้มากขึ้นสำหรับกรณีทั่วไปแทนที่จะเป็นตัวอย่างเฉพาะที่ใช้เพื่ออธิบายคำถามอย่างชัดเจน
user2259664

22

คุณสามารถตรวจสอบว่าทางแยกว่างเปล่าหรือไม่

cheeses = %w(chedder stilton brie mozzarella feta haloumi)
foods = %w(pizza feta foods bread biscuits yoghurt bacon)
foods & cheeses
=> ["feta"] 
(foods & cheeses).empty?
=> false

1
Set.new(cheeses).disjoint? Set.new(foods)

นอกจากนี้ในเกณฑ์มาตรฐานของฉัน (unscientific) set disjoint นั้นช้ากว่าวิธีอื่น ๆ อย่างมาก: gist.github.com/jaredmoody/d2a1e83de2f91fd6865920cd01a8b497
Jared

1
ขอบคุณสำหรับความคิดเห็นของคุณ ฉันไม่แน่ใจว่าทำไมมันไม่ Set.new แต่ฉันเพิ่งแก้ไขมัน ฉันลองทดสอบประสิทธิภาพของคุณใน 2.4.1 ของฉันทำได้ดีกว่า แต่ก็ยังไม่ดีที่สุดโดยใช้ชุดที่แยกจากกันซึ่งมีคำมากกว่านี้ ฉันใส่ความคิดเห็นของฉันลงบนส่วนสำคัญของคุณ ฉันยังคิดว่าdisjoint?สง่างามมากโดยเฉพาะเมื่อเทียบกับ "ใด ๆ รวม?" คำถามเดิมถามเกี่ยวกับทั้งความสวยงามและมีประสิทธิภาพ
davidkovsky

.to_setวิธีนี้มีประโยชน์ที่นี่cheeses.to_set.disjoint?(foods.to_set)
itsnikolay

0
require "benchmark"
N = 1_000_000
puts "ruby version: #{RUBY_VERSION}"

CHEESES = %w(chedder stilton brie mozzarella feta haloumi).freeze
FOODS = %w(pizza feta foods bread biscuits yoghurt bacon).freeze

Benchmark.bm(15) do |b|
  b.report("&, empty?") { N.times { (FOODS & CHEESES).empty? } }  
  b.report("any?, include?") { N.times { FOODS.any? {|food| CHEESES.include?(food) } } }  
  b.report("disjoint?") { N.times { FOODS.to_set.disjoint? CHEESES.to_set }}
end  
                      user     system      total        real
&, empty?         0.751068   0.000571   0.751639 (  0.752745)
any?, include?    0.408251   0.000133   0.408384 (  0.408438)
disjoint?        11.616006   0.014806  11.630812 ( 11.637300)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.