นี่คืออีกสองวิธีในการค้นหาสิ่งที่ซ้ำกัน
ใช้ชุด
require 'set'
def find_a_dup_using_set(arr)
s = Set.new
arr.find { |e| !s.add?(e) }
end
find_a_dup_using_set arr
#=> "hello"
ใช้select
แทนfind
การส่งกลับอาร์เรย์ของรายการที่ซ้ำกันทั้งหมด
ใช้ Array#difference
class Array
def difference(other)
h = other.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
reject { |e| h[e] > 0 && h[e] -= 1 }
end
end
def find_a_dup_using_difference(arr)
arr.difference(arr.uniq).first
end
find_a_dup_using_difference arr
#=> "hello"
วาง.first
เพื่อส่งกลับอาร์เรย์ของรายการซ้ำทั้งหมด
ทั้งสองวิธีจะส่งคืนnil
หากไม่มีรายการที่ซ้ำกัน
ฉันเสนอว่าArray#difference
จะเพิ่มเข้าไปในแกนทับทิม ข้อมูลเพิ่มเติมในคำตอบของฉันที่นี่
เกณฑ์มาตรฐาน
ลองเปรียบเทียบวิธีที่แนะนำ อันดับแรกเราต้องมีอาร์เรย์สำหรับการทดสอบ:
CAPS = ('AAA'..'ZZZ').to_a.first(10_000)
def test_array(nelements, ndups)
arr = CAPS[0, nelements-ndups]
arr = arr.concat(arr[0,ndups]).shuffle
end
และวิธีการรันมาตรฐานสำหรับอาร์เรย์ทดสอบที่แตกต่างกัน:
require 'fruity'
def benchmark(nelements, ndups)
arr = test_array nelements, ndups
puts "\n#{ndups} duplicates\n"
compare(
Naveed: -> {arr.detect{|e| arr.count(e) > 1}},
Sergio: -> {(arr.inject(Hash.new(0)) {|h,e| h[e] += 1; h}.find {|k,v| v > 1} ||
[nil]).first },
Ryan: -> {(arr.group_by{|e| e}.find {|k,v| v.size > 1} ||
[nil]).first},
Chris: -> {arr.detect {|e| arr.rindex(e) != arr.index(e)} },
Cary_set: -> {find_a_dup_using_set(arr)},
Cary_diff: -> {find_a_dup_using_difference(arr)}
)
end
ฉันไม่ได้รวมคำตอบของ @ JjP ไว้เพราะจะส่งคืนสำเนาเดียวเท่านั้นและเมื่อคำตอบของเขา / เธอถูกแก้ไขเพื่อให้เป็นเช่นเดียวกับคำตอบก่อนหน้านี้ของ @ Naveed ฉันไม่ได้รวมคำตอบของ @ Marin ไว้ด้วยซึ่งในขณะที่โพสต์ก่อนที่คำตอบของ @ Naveed จะส่งคืนรายการที่ซ้ำกันทั้งหมดแทนที่จะเป็นเพียงรายการเดียว (เป็นจุดรอง แต่ไม่มีประเด็นที่ประเมินทั้งคู่
ฉันยังแก้ไขคำตอบอื่น ๆ ที่ส่งคืนข้อมูลที่ซ้ำกันทั้งหมดเพื่อส่งกลับไปยังคำตอบแรกที่พบ แต่ไม่ควรส่งผลกระทบต่อประสิทธิภาพเนื่องจากพวกเขาคำนวณรายการที่ซ้ำกันทั้งหมดก่อนเลือก
ผลลัพธ์สำหรับแต่ละเกณฑ์มาตรฐานแสดงรายการจากเร็วที่สุดไปช้าที่สุด:
ขั้นแรกสมมติว่าอาร์เรย์มี 100 องค์ประกอบ:
benchmark(100, 0)
0 duplicates
Running each test 64 times. Test will take about 2 seconds.
Cary_set is similar to Cary_diff
Cary_diff is similar to Ryan
Ryan is similar to Sergio
Sergio is faster than Chris by 4x ± 1.0
Chris is faster than Naveed by 2x ± 1.0
benchmark(100, 1)
1 duplicates
Running each test 128 times. Test will take about 2 seconds.
Cary_set is similar to Cary_diff
Cary_diff is faster than Ryan by 2x ± 1.0
Ryan is similar to Sergio
Sergio is faster than Chris by 2x ± 1.0
Chris is faster than Naveed by 2x ± 1.0
benchmark(100, 10)
10 duplicates
Running each test 1024 times. Test will take about 3 seconds.
Chris is faster than Naveed by 2x ± 1.0
Naveed is faster than Cary_diff by 2x ± 1.0 (results differ: AAC vs AAF)
Cary_diff is similar to Cary_set
Cary_set is faster than Sergio by 3x ± 1.0 (results differ: AAF vs AAC)
Sergio is similar to Ryan
ตอนนี้ให้พิจารณาอาร์เรย์ที่มีองค์ประกอบ 10,000 รายการ:
benchmark(10000, 0)
0 duplicates
Running each test once. Test will take about 4 minutes.
Ryan is similar to Sergio
Sergio is similar to Cary_set
Cary_set is similar to Cary_diff
Cary_diff is faster than Chris by 400x ± 100.0
Chris is faster than Naveed by 3x ± 0.1
benchmark(10000, 1)
1 duplicates
Running each test once. Test will take about 1 second.
Cary_set is similar to Cary_diff
Cary_diff is similar to Sergio
Sergio is similar to Ryan
Ryan is faster than Chris by 2x ± 1.0
Chris is faster than Naveed by 2x ± 1.0
benchmark(10000, 10)
10 duplicates
Running each test once. Test will take about 11 seconds.
Cary_set is similar to Cary_diff
Cary_diff is faster than Sergio by 3x ± 1.0 (results differ: AAE vs AAA)
Sergio is similar to Ryan
Ryan is faster than Chris by 20x ± 10.0
Chris is faster than Naveed by 3x ± 1.0
benchmark(10000, 100)
100 duplicates
Cary_set is similar to Cary_diff
Cary_diff is faster than Sergio by 11x ± 10.0 (results differ: ADG vs ACL)
Sergio is similar to Ryan
Ryan is similar to Chris
Chris is faster than Naveed by 3x ± 1.0
โปรดทราบว่าfind_a_dup_using_difference(arr)
จะมีประสิทธิภาพมากขึ้นหากArray#difference
มีการใช้งานใน C ซึ่งจะเป็นกรณีถ้ามันถูกเพิ่มเข้าไปในแกนทับทิม
ข้อสรุป
หลายคำตอบที่มีความเหมาะสม แต่ใช้ชุดเป็นตัวเลือกที่ดีที่สุดที่ชัดเจน มันเร็วที่สุดในกรณียากปานกลาง - ปานกลางเร็วที่สุดในกรณีที่ยากที่สุดและเฉพาะในกรณีที่ประมวลผลได้เล็กน้อย - เมื่อตัวเลือกของคุณไม่สำคัญ
กรณีพิเศษอย่างหนึ่งที่คุณอาจเลือกโซลูชันของ Chris คือถ้าคุณต้องการใช้วิธีแยกอาร์เรย์ขนาดเล็กหลายพันชุดโดยไม่ซ้ำกันและคาดว่าจะหารายการซ้ำโดยทั่วไปน้อยกว่า 10 รายการระบบจะเร็วขึ้นเล็กน้อย เนื่องจากหลีกเลี่ยงค่าใช้จ่ายเพิ่มเติมเล็กน้อยในการสร้างชุด
arr == arr.uniq
จะเป็นวิธีที่ง่ายและสง่างามในการตรวจสอบว่าarr
มีรายการที่ซ้ำกัน แต่ไม่ได้ระบุว่ามีรายการใดที่ซ้ำกัน