วิธีการตรวจสอบว่าหนึ่งอาร์เรย์มีองค์ประกอบทั้งหมดของอาร์เรย์อื่น


179

ได้รับ:

a1 = [5, 1, 6, 14, 2, 8]

ฉันต้องการตรวจสอบว่ามันมีองค์ประกอบทั้งหมดของ:

a2 = [2, 6, 15]

falseในกรณีนี้ผลที่ได้คือ

มีวิธี Ruby / Rails ในตัวเพื่อระบุการรวมอาเรย์ดังกล่าวหรือไม่?

วิธีหนึ่งในการดำเนินการนี้คือ:

a2.index{ |x| !a1.include?(x) }.nil?

มีวิธีที่ดีกว่าอ่านได้มากขึ้นหรือไม่


คำตอบที่ยอมรับได้ (การลบอาเรย์) เป็นวิธีที่เร็วที่สุด ฉันเปรียบเทียบพวกเขาทั้งหมดที่นี่: gist.github.com/bbugh/cbbde8b48cbb16286044f6893e1f2e5f
brainbag

คำตอบ:


309
a = [5, 1, 6, 14, 2, 8]
b = [2, 6, 15]

a - b
=> [5, 1, 14, 8]

b - a
=> [15]

(b - a).empty?
=> false

61
นี่คือหนทางที่จะไป มันอาจจะสั้นไปสักหน่อย(a2-a1).empty?
Holger เพียง

9
ใช้งานได้กับอาร์เรย์ที่มีการตั้งค่าเท่านั้นไม่ใช่สำหรับอาร์เรย์ที่มีซ้ำกัน
Chris

3
@Chris - คุณสามารถลองใช้ Array # uniq ได้ ด้วยตัวอย่างของ Holger Just มันจะเป็น(a2.uniq - a1.uniq).empty?
Nick

stackoverflow.com/questions/13553822/…เป็นสิ่งที่ฉันหมายถึง Array # unique จะล้มเหลวอย่างชัดเจน
คริส

81

บางทีนี่อาจอ่านง่ายกว่า:

a2.all? { |e| a1.include?(e) }

คุณยังสามารถใช้จุดตัดของอาร์เรย์:

(a1 & a2).size == a1.size

โปรดทราบว่าsizeใช้ที่นี่เพื่อความเร็วเท่านั้นคุณยังสามารถทำได้ (ช้ากว่า):

(a1 & a2) == a1

แต่ฉันเดาว่าอันแรกอ่านได้ง่ายกว่า 3 เหล่านี้เป็นทับทิมธรรมดา (ไม่ใช่ราง)


หากใช้คำจำกัดความของ OP ของ a1 และ a2 และ a1 "ที่มีองค์ประกอบทั้งหมดของ" a2 ฉันคิดว่านี่ควรเป็น _ (a1 & a2) .size == a2.size _ เนื่องจาก a2 เป็นอาร์เรย์ที่เล็กกว่าซึ่งควรมีทั้งหมด องค์ประกอบที่รวมอยู่ในอาร์เรย์ที่ใหญ่กว่า (เพื่อให้ได้ 'จริง') - ดังนั้นการตัดกันของสองอาร์เรย์ควรมีความยาวเท่ากับอาร์เรย์ขนาดเล็กถ้าองค์ประกอบทั้งหมดในนั้นมีอยู่ในอันที่ใหญ่กว่า
JosephK

57

สิ่งนี้สามารถทำได้โดยการทำ

(a2 & a1) == a2

สิ่งนี้จะสร้างจุดตัดของทั้งสองอาร์เรย์โดยส่งคืนองค์ประกอบทั้งหมดa2ที่อยู่ในa1นั้นด้วย หากผลลัพธ์เหมือนกันa2คุณสามารถมั่นใจได้ว่าคุณมีองค์ประกอบทั้งหมดรวมอยู่a1ด้วย

วิธีการนี้จะใช้งานได้ก็ต่อเมื่อองค์ประกอบทั้งหมดในa2นั้นแตกต่างจากกันในตอนแรก หากมีคู่วิธีนี้ล้มเหลว ส่วนหนึ่งจาก Tempos ยังคงใช้งานได้ดังนั้นฉันขอแนะนำวิธีการของเขาอย่างเต็มที่ (ซึ่งอาจเร็วกว่า)


2
การใช้lengthวิธีนี้จะทำได้ดีกว่ามาก
Pablo Fernandez

3
สิ่งนี้จะไม่ทำงานหากชุดจุดตัดมีองค์ประกอบเหมือนกันในลำดับที่แตกต่างกัน ฉันพบสิ่งนี้ยากในขณะที่พยายามตอบคำถามนี้: stackoverflow.com/questions/12062970/…ต่อมาตระหนักว่าคนฉลาดจำนวนมากได้ทำมันที่นี่แล้ว!
CubaLibre

1
@CubaLibre ที่น่าสนใจ คุณมีข้อมูลการทดสอบที่จะทำซ้ำนี้หรือไม่? การทดสอบของฉันดูเหมือนว่าอาร์เรย์ผลลัพธ์จะรักษาลำดับขององค์ประกอบจากอาร์เรย์แรก (ดังนั้นการแก้ไขล่าสุดของฉันสำหรับคำตอบของฉัน) อย่างไรก็ตามหากนี่ไม่ใช่กรณีจริง ๆ ฉันต้องการเรียนรู้
Holger เพิ่ง

@ โฮลเกอร์เพียงแค่ฉันทำผิดพลาดในการทำ (a1 & a2) แทน (a2 & a1) ซึ่งเป็นสาเหตุที่ฉันเห็นข้อผิดพลาด คุณมีสิทธิ์เกี่ยวกับ & คงคำสั่งซื้อจากอาร์เรย์แรก
CubaLibre

10

หากไม่มีองค์ประกอบที่ซ้ำกันหรือคุณไม่สนใจพวกเขาคุณสามารถใช้คลาสSet :

a1 = Set.new [5, 1, 6, 14, 2, 8]
a2 = Set.new [2, 6, 15]
a1.subset?(a2)
=> false

เบื้องหลังสิ่งนี้ใช้

all? { |o| set.include?(o) }

1

คุณสามารถปรับแต่ง Array class:

class Array
    def contains_all?(ary)
        ary.uniq.all? { |x| count(x) >= ary.count(x) }
    end
end

ทดสอบ

irb(main):131:0> %w[a b c c].contains_all? %w[a b c]
=> true
irb(main):132:0> %w[a b c c].contains_all? %w[a b c c]
=> true
irb(main):133:0> %w[a b c c].contains_all? %w[a b c c c]
=> false
irb(main):134:0> %w[a b c c].contains_all? %w[a]
=> true
irb(main):135:0> %w[a b c c].contains_all? %w[x]
=> false
irb(main):136:0> %w[a b c c].contains_all? %w[]
=> true
irb(main):137:0> %w[a b c d].contains_all? %w[d c h]
=> false
irb(main):138:0> %w[a b c d].contains_all? %w[d b c]
=> true

แน่นอนว่าวิธีการนั้นสามารถเขียนเป็นวิธีมาตรฐานได้เช่น

def contains_all?(a,b)
    b.uniq.all? { |x| a.count(x) >= b.count(x) }
end

และคุณสามารถเรียกมันเช่น

contains_all?(%w[a b c c], %w[c c c])

แน่นอนหลังจากทำโปรไฟล์แล้วเวอร์ชั่นต่อไปนี้จะเร็วกว่ามากและรหัสสั้นกว่า

def contains_all?(a,b)
    b.all? { |x| a.count(x) >= b.count(x) }
end

0

ขึ้นอยู่กับว่าอาร์เรย์ของคุณใหญ่แค่ไหนคุณอาจพิจารณาอัลกอริทึมที่มีประสิทธิภาพ O (n log n)

def equal_a(a1, a2)
  a1sorted = a1.sort
  a2sorted = a2.sort
  return false if a1.length != a2.length
  0.upto(a1.length - 1) do 
    |i| return false if a1sorted[i] != a2sorted[i]
  end
end

การเรียงลำดับต้นทุน O (n log n) และการตรวจสอบแต่ละคู่มีค่า O (n) ดังนั้นอัลกอริทึมนี้คือ O (n log n) อัลกอริทึมอื่นไม่สามารถเร็วขึ้น (asymptotically) โดยใช้อาร์เรย์ที่ไม่เรียงลำดับ


คุณสามารถทำได้ใน O (n) ด้วยการเรียงลำดับการนับ
klochner

ไม่คุณไม่สามารถ. การเรียงลำดับการนับใช้เอกภพที่มี จำกัด และทับทิมไม่มีข้อ จำกัด ว่าจะมีจำนวนมากแค่ไหน
ayckoster

คุณสามารถทำได้เพราะคุณไม่จำเป็นต้องเรียงลำดับรายการ - คุณเพียงแค่ต้องใช้รายการการจับคู่แฮช -> นับสำหรับทั้งสองอาร์เรย์จากนั้นทำซ้ำคีย์และเปรียบเทียบจำนวน
klochner

คุณแน่ใจหรือไม่ว่า # Array # sort ใช้การผสานการจัดเรียง?
Nate Symer

0

คำตอบส่วนใหญ่จาก (a1 - a2) หรือ (a1 & a2) จะไม่ทำงานหากมีองค์ประกอบที่ซ้ำกันในอาร์เรย์ทั้งสอง ฉันมาถึงที่นี่เพื่อหาวิธีดูว่าตัวอักษรทั้งหมดของคำ (แยกเป็นอาร์เรย์) เป็นส่วนหนึ่งของชุดตัวอักษร (ตัวอย่างเช่นข่วน) คำตอบเหล่านี้ไม่ได้ผล แต่คำตอบนี้ใช้ได้:

def contains_all?(a1, a2)
  try = a1.chars.all? do |letter|
    a1.count(letter) <= a2.count(letter)
  end
  return try
end
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.