วิธีการค้นหาและคืนค่าที่ซ้ำกันในอาร์เรย์


170

arr คืออาร์เรย์ของสตริง:

["hello", "world", "stack", "overflow", "hello", "again"]

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

ตัวอย่าง:

["A", "B", "C", "B", "A"]    # => "A" or "B"
["A", "B", "C"]              # => nil

arr == arr.uniqจะเป็นวิธีที่ง่ายและสง่างามในการตรวจสอบว่าarrมีรายการที่ซ้ำกัน แต่ไม่ได้ระบุว่ามีรายการใดที่ซ้ำกัน
Joel AZEMAR

คำตอบ:


249
a = ["A", "B", "C", "B", "A"]
a.detect{ |e| a.count(e) > 1 }

ฉันรู้ว่านี่ไม่ใช่คำตอบที่หรูหรามาก แต่ฉันรักมัน มันเป็นโค้ดซับอันหนึ่งที่สวยงาม และทำงานได้อย่างสมบูรณ์แบบหากคุณไม่จำเป็นต้องประมวลผลชุดข้อมูลขนาดใหญ่

กำลังมองหาวิธีแก้ปัญหาที่เร็วกว่าใช่ไหม ไปเลย!

def find_one_using_hash_map(array)
  map = {}
  dup = nil
  array.each do |v|
    map[v] = (map[v] || 0 ) + 1

    if map[v] > 1
      dup = v
      break
    end
  end

  return dup
end

มันเป็นแบบเชิงเส้น, O (n), แต่ตอนนี้จำเป็นต้องจัดการหลายบรรทัดของรหัส, ต้องการกรณีทดสอบ, ฯลฯ

หากคุณต้องการวิธีแก้ปัญหาที่เร็วกว่านี้ให้ลองใช้ C แทน

และนี่คือส่วนสำคัญที่เปรียบเทียบโซลูชันที่แตกต่าง: https://gist.github.com/naveed-ahmad/8f0b926ffccf5fbd206a1cc58ce9743e


59
ยกเว้นกำลังสองสำหรับสิ่งที่สามารถแก้ไขได้ในเวลาเชิงเส้น
jasonmp85

18
การให้คำตอบ O (n ^ 2) สำหรับปัญหาเชิงเส้นไม่ใช่วิธีที่จะไป
tdgs

21
@ jasonmp85 - จริง; อย่างไรก็ตามนั่นเป็นเพียงการพิจารณารันไทม์ใหญ่ของ O ในทางปฏิบัติยกเว้นว่าคุณกำลังเขียนโค้ดนี้สำหรับข้อมูลขนาดใหญ่บางส่วน (และถ้าเป็นเช่นนั้นคุณสามารถใช้ C หรือ Python ได้จริง ๆ ) คำตอบที่ให้ไว้นั้นสวยงาม / อ่านได้มากกว่าและจะไม่ทำงานช้ากว่ามาก เพื่อแก้ปัญหาเวลาเชิงเส้น ยิ่งไปกว่านั้นในทางทฤษฎีการแก้ปัญหาเชิงเส้นเวลาต้องการพื้นที่เชิงเส้นซึ่งอาจไม่สามารถใช้ได้
David T.

26
@Kalanamith คุณสามารถรับค่าซ้ำโดยใช้สิ่งนี้a.select {|e| a.count(e) > 1}.uniq
Naveed

26
ปัญหาของเมธอด "detect" คือหยุดเมื่อพบสำเนาแรกและไม่ให้สำเนาทั้งหมด
Jaime Bellmyer

214

คุณสามารถทำได้สองสามวิธีโดยที่ตัวเลือกแรกจะเร็วที่สุด:

ary = ["A", "B", "C", "B", "A"]

ary.group_by{ |e| e }.select { |k, v| v.size > 1 }.map(&:first)

ary.sort.chunk{ |e| e }.select { |e, chunk| chunk.size > 1 }.map(&:first)

และตัวเลือก O (N ^ 2) (เช่นมีประสิทธิภาพน้อยกว่า):

ary.select{ |e| ary.count(e) > 1 }.uniq

17
สองตัวแรกมีประสิทธิภาพมากขึ้นสำหรับอาร์เรย์ขนาดใหญ่ อันสุดท้ายคือ O (n * n) เพื่อให้ช้าลง ฉันต้องการใช้สิ่งนี้กับอาร์เรย์ที่มีองค์ประกอบ ~ 20k และสองรายการแรกกลับมาเกือบจะทันที ฉันต้องยกเลิกอันที่สามเพราะมันใช้เวลานานมาก ขอบคุณ !!
Venkat D.

5
เพียงแค่การสังเกต แต่สองคนแรกที่ลงท้ายด้วย. map (&: แรก)อาจลงท้ายด้วย. keyเนื่องจากส่วนนั้นเพิ่งดึงกุญแจบนแฮช
engineerDave

@engineerDave ที่ขึ้นอยู่กับรุ่นทับทิมที่ใช้ 1.8.7 จะต้อง &: ก่อนหรือแม้แต่ {| k, _ | k} โดยไม่มี ActiveSupport
Emirikol

นี่คือมาตรฐานบางอย่างgist.github.com/equivalent/3c9a4c9d07fff79062a3 ในการแสดงผู้ชนะชัดเจน group_by.select
เทียบเท่า

6
หากคุณกำลังใช้ทับทิม> 2.1, ary.group_by(&:itself)คุณสามารถใช้: :-)
Drenmi

44

เพียงค้นหาอินสแตนซ์แรกที่ดัชนีของวัตถุ (นับจากทางซ้าย) ไม่เท่ากับดัชนีของวัตถุ (นับจากทางขวา)

arr.detect {|e| arr.rindex(e) != arr.index(e) }

หากไม่มีการซ้ำซ้อนค่าตอบแทนจะเป็นศูนย์

ผมเชื่อว่านี่เป็นวิธีที่เร็วที่สุดที่โพสต์ในหัวข้อเพื่อให้ห่างไกลเช่นกันเพราะมันไม่ได้ขึ้นอยู่กับการสร้างวัตถุเพิ่มเติมและ#indexและ#rindexจะดำเนินการในซีรันไทม์ใหญ่-O คือ N ^ 2 และทำให้ช้ากว่า Sergio's แต่เวลาบนกำแพงอาจเร็วกว่ามากเนื่องจากความจริงที่ว่าชิ้นส่วน "ช้า" ทำงานในซี


5
ฉันชอบวิธีนี้ แต่จะคืนค่าสำเนาแรกเท่านั้น ในการค้นหารายการที่ซ้ำกันทั้งหมด:arr.find_all {|e| arr.rindex(e) != arr.index(e) }.uniq
Josh

1
คำตอบของคุณไม่แสดงวิธีการค้นหาว่ามี triplicates หรือไม่ว่าใครสามารถวาดองค์ประกอบจากอาร์เรย์เพื่อสะกด "CAT"
Cary Swoveland

3
@ bruno077 เวลาเชิงเส้นเป็นอย่างไร?
beauby

4
@ Chris คำตอบที่ดี arr.detect.with_index { |e, idx| idx != arr.rindex(e) }แต่ฉันคิดว่าคุณสามารถทำได้ดีขึ้นเล็กน้อยด้วยนี้ การใช้with_indexควรลบความจำเป็นสำหรับการindexค้นหาครั้งแรก
ki4jnq

คุณจะปรับสิ่งนี้ให้เป็นอาร์เรย์ 2 มิติโดยเปรียบเทียบการทำซ้ำในคอลัมน์ได้อย่างไร
ahnbizcad

30

detectค้นหาหนึ่งรายการที่ซ้ำกันเท่านั้น find_allจะพบพวกเขาทั้งหมด:

a = ["A", "B", "C", "B", "A"]
a.find_all { |e| a.count(e) > 1 }

3
คำถามนี้เฉพาะเจาะจงมากที่จะส่งคืนสำเนาเพียงหนึ่งรายการเท่านั้น Imo การแสดงวิธีการค้นหารายการที่ซ้ำกันทั้งหมดนั้นใช้ได้ แต่เป็นเพียงคำตอบที่ตอบคำถามที่คุณยังไม่ได้ทำ btw มันไม่มีประสิทธิภาพที่จะทนทุกข์ทรมานcountสำหรับทุกองค์ประกอบในอาร์เรย์ (ตัวอย่างเช่นแฮชการนับมีประสิทธิภาพมากกว่าเช่นสร้างh = {"A"=>2, "B"=>2, "C"=> 1 }ตอนh.select { |k,v| v > 1 }.keys #=> ["A", "B"]นี้
Cary Swoveland

24

นี่คืออีกสองวิธีในการค้นหาสิ่งที่ซ้ำกัน

ใช้ชุด

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 รายการระบบจะเร็วขึ้นเล็กน้อย เนื่องจากหลีกเลี่ยงค่าใช้จ่ายเพิ่มเติมเล็กน้อยในการสร้างชุด


1
ทางออกที่ดีเยี่ยม มันไม่ค่อยชัดเจนว่าเกิดอะไรขึ้นในตอนแรกกับบางวิธี แต่มันควรจะทำงานในเวลาเชิงเส้นอย่างแท้จริงด้วยค่าใช้จ่ายของหน่วยความจำเล็กน้อย
Chris Heald

ด้วย find_a_dup_using_set ฉันได้รับ Set back แทนที่จะเป็นหนึ่งในรายการที่ซ้ำกัน นอกจากนี้ฉันไม่พบ "find.with_object" ใน Ruby docs ทุกที่
ScottJ

@Scottj ขอบคุณสำหรับการจับ! มันน่าสนใจที่ไม่มีใครจับได้เมื่อก่อน ฉันซ่อมมัน. ที่Enumerable # หาล่ามโซ่ไว้กับการแจงนับ # with_object ฉันจะอัปเดตการวัดประสิทธิภาพเพิ่มโซลูชันของคุณและอื่น ๆ
Cary Swoveland

1
การเปรียบเทียบที่ยอดเยี่ยม @CarySwoveland
Naveed

19

O(n^2)อนิจจาที่สุดของคำตอบจะ

นี่คือO(n)ทางออก

a = %w{the quick brown fox jumps over the lazy dog}
h = Hash.new(0)
a.find { |each| (h[each] += 1) == 2 } # => 'the"

ความซับซ้อนของสิ่งนี้คืออะไร?

  • วิ่งเข้าO(n)และออกจากการแข่งขันนัดแรก
  • ใช้O(n)หน่วยความจำ แต่เพียงจำนวนน้อยที่สุด

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


15

selectทับทิมวัตถุอาร์เรย์มีวิธีการที่ดี

select {|item| block }  new_ary
select  an_enumerator

แบบฟอร์มแรกคือสิ่งที่คุณสนใจที่นี่ ช่วยให้คุณสามารถเลือกวัตถุที่ผ่านการทดสอบ

วัตถุ Ruby Array มีวิธีอื่น, count.

count  int
count(obj)  int
count { |item| block }  int

ในกรณีนี้คุณมีความสนใจในการทำซ้ำ (วัตถุที่ปรากฏมากกว่าหนึ่งครั้งในอาร์เรย์) a.count(obj) > 1การทดสอบที่เหมาะสมคือ

ถ้าเป็นa = ["A", "B", "C", "B", "A"]เช่นนั้น

a.select{|item| a.count(item) > 1}.uniq
=> ["A", "B"]

คุณระบุว่าคุณต้องการเพียงหนึ่งวัตถุ ดังนั้นเลือกหนึ่งอัน


1
ฉันชอบอันนี้มาก แต่คุณต้องโยน uniq ในตอนท้ายหรือคุณจะได้รับ["A", "B", "B", "A"]
Joeyjoejoejr

1
คำตอบที่ดี นี่คือสิ่งที่ฉันกำลังมองหา @Joeyjoejoejr ชี้ให้เห็น ฉันได้ส่งการแก้ไขเพื่อใส่.uniqอาร์เรย์
Surya

สิ่งนี้ไม่มีประสิทธิภาพอย่างมหาศาล ไม่เพียง แต่คุณจะพบรายการที่ซ้ำกันทั้งหมดและจากนั้นทิ้งไปทั้งหมด แต่มีรายการเดียวที่คุณเรียกใช้countสำหรับแต่ละองค์ประกอบของอาร์เรย์ซึ่งสิ้นเปลืองและไม่จำเป็น ดูความคิดเห็นของฉันในคำตอบของ JjP
Cary Swoveland

ขอขอบคุณที่ใช้การวัดประสิทธิภาพ มันจะมีประโยชน์ที่จะเห็นว่าโซลูชั่นที่แตกต่างเปรียบเทียบในเวลาทำงาน คำตอบที่สง่างามสามารถอ่านได้ แต่มักจะไม่ได้ผลที่สุด
Martin Velez

9

find_all ()กลับarrayมีองค์ประกอบทั้งหมดของenumที่ไม่ได้เป็นblockfalse

เพื่อให้ได้duplicateองค์ประกอบ

>> arr = ["A", "B", "C", "B", "A"]
>> arr.find_all { |x| arr.count(x) > 1 }

=> ["A", "B", "B", "A"]

หรือuniqองค์ประกอบที่ซ้ำกัน

>> arr.find_all { |x| arr.count(x) > 1 }.uniq
=> ["A", "B"] 

7

บางสิ่งเช่นนี้จะได้ผล

arr = ["A", "B", "C", "B", "A"]
arr.inject(Hash.new(0)) { |h,e| h[e] += 1; h }.
    select { |k,v| v > 1 }.
    collect { |x| x.first }

กล่าวคือให้ใส่ค่าทั้งหมดลงในแฮชที่สำคัญคือองค์ประกอบของอาร์เรย์และค่าคือจำนวนครั้งที่เกิดขึ้น จากนั้นเลือกองค์ประกอบทั้งหมดที่เกิดขึ้นมากกว่าหนึ่งครั้ง ง่าย.


7

ฉันรู้ว่ากระทู้นี้เกี่ยวกับ Ruby โดยเฉพาะ แต่ฉันมาถึงที่นี่เพื่อค้นหาวิธีการทำสิ่งนี้ภายในบริบทของ Ruby on Rails ด้วย ActiveRecord และคิดว่าฉันจะแบ่งปันโซลูชันด้วย

class ActiveRecordClass < ActiveRecord::Base
  #has two columns, a primary key (id) and an email_address (string)
end

ActiveRecordClass.group(:email_address).having("count(*) > 1").count.keys

ด้านบนส่งคืนอาร์เรย์ของที่อยู่อีเมลทั้งหมดที่ซ้ำกันในตารางฐานข้อมูลของตัวอย่างนี้ (ซึ่งใน Rails จะเป็น "active_record_classes")


6
a = ["A", "B", "C", "B", "A"]
a.each_with_object(Hash.new(0)) {|i,hash| hash[i] += 1}.select{|_, count| count > 1}.keys

นี่เป็นO(n)ขั้นตอน

หรือคุณสามารถเลือกทำอย่างใดอย่างหนึ่งของบรรทัดต่อไปนี้ นอกจากนี้ O (n) แต่มีการวนซ้ำเพียงครั้งเดียว

a.each_with_object(Hash.new(0).merge dup: []){|x,h| h[:dup] << x if (h[x] += 1) == 2}[:dup]

a.inject(Hash.new(0).merge dup: []){|h,x| h[:dup] << x if (h[x] += 1) == 2;h}[:dup]

2

นี่คือการนำของฉันไปใช้กับชุดข้อมูลขนาดใหญ่ - เช่นตาราง dBase ดั้งเดิมเพื่อค้นหาชิ้นส่วนที่ซ้ำกัน

# Assuming ps is an array of 20000 part numbers & we want to find duplicates
# actually had to it recently.
# having a result hash with part number and number of times part is 
# duplicated is much more convenient in the real world application
# Takes about 6  seconds to run on my data set
# - not too bad for an export script handling 20000 parts

h = {};

# or for readability

h = {} # result hash
ps.select{ |e| 
  ct = ps.count(e) 
  h[e] = ct if ct > 1
}; nil # so that the huge result of select doesn't print in the console


1

each_with_object เป็นเพื่อนของคุณ!

input = [:bla,:blubb,:bleh,:bla,:bleh,:bla,:blubb,:brrr]

# to get the counts of the elements in the array:
> input.each_with_object({}){|x,h| h[x] ||= 0; h[x] += 1}
=> {:bla=>3, :blubb=>2, :bleh=>2, :brrr=>1}

# to get only the counts of the non-unique elements in the array:
> input.each_with_object({}){|x,h| h[x] ||= 0; h[x] += 1}.reject{|k,v| v < 2}
=> {:bla=>3, :blubb=>2, :bleh=>2}

1

รหัสนี้จะส่งคืนรายการค่าที่ซ้ำกัน ปุ่มแฮชใช้เป็นวิธีที่มีประสิทธิภาพในการตรวจสอบว่าได้เห็นค่าใดบ้าง ขึ้นอยู่กับว่ามีการเห็นค่าหรือไม่อาร์เรย์ดั้งเดิมaryจะถูกแบ่งพาร์ติชันออกเป็น 2 อาร์เรย์: ก่อนประกอบด้วยค่าที่ไม่ซ้ำกันและอันดับที่สองมีการทำซ้ำ

ary = ["hello", "world", "stack", "overflow", "hello", "again"]

hash={}
arr.partition { |v| hash.has_key?(v) ? false : hash[v]=0 }.last.uniq

=> ["hello"]

คุณสามารถย่อให้สั้นลง - แม้ว่าจะมีค่าใช้จ่ายที่ซับซ้อนกว่าเล็กน้อยจากรูปแบบนี้:

hash={}
arr.partition { |v| !hash.has_key?(v) && hash[v]=0 }.last.uniq


0

หากคุณกำลังเปรียบเทียบสองอาร์เรย์ที่แตกต่างกัน (แทนที่จะเป็นคนเดียวกับตัวเอง) เป็นวิธีที่รวดเร็วมากคือการใช้ตัวดำเนินการตัด&ให้โดยArray ชั้นทับทิม

# Given
a = ['a', 'b', 'c', 'd']
b = ['e', 'f', 'c', 'd']

# Then this...
a & b # => ['c', 'd']

1
ที่ค้นหารายการที่มีอยู่ในทั้งสองอาร์เรย์ไม่ซ้ำกันในหนึ่งอาร์เรย์
Kimmo Lehto

ขอบคุณสำหรับการชี้ให้เห็นว่า ฉันเปลี่ยนถ้อยคำในคำตอบของฉัน ฉันจะออกจากที่นี่เพราะมันได้รับการพิสูจน์แล้วว่ามีประโยชน์สำหรับบางคนที่มาจากการค้นหา
IAmNaN

0

ฉันต้องการค้นหาว่ามีซ้ำกันกี่ครั้งและพวกเขาเป็นเช่นไรฉันจึงเขียนฟังก์ชั่นการสร้างสิ่งที่ Naveed โพสต์ไว้ก่อนหน้านี้:

def print_duplicates(array)
  puts "Array count: #{array.count}"
  map = {}
  total_dups = 0
  array.each do |v|
    map[v] = (map[v] || 0 ) + 1
  end

  map.each do |k, v|
    if v != 1
      puts "#{k} appears #{v} times"
      total_dups += 1
    end
  end
  puts "Total items that are duplicated: #{total_dups}"
end

-1
  1. ลองสร้างวิธีการทำซ้ำที่ใช้อาร์เรย์ขององค์ประกอบเป็นอินพุต
  2. ในส่วนของเมธอดลองสร้างวัตถุอาร์เรย์ใหม่ 2 อันที่เห็นและอีกอันหนึ่งซ้ำกัน
  3. ในที่สุดก็อนุญาตให้วนซ้ำแต่ละวัตถุในอาร์เรย์ที่กำหนดและสำหรับทุกการวนซ้ำให้ค้นหาวัตถุนั้นที่มีอยู่ในอาร์เรย์ที่เห็น
  4. หากวัตถุมีอยู่ใน seen_array ก็จะถือว่าเป็นวัตถุที่ซ้ำกันและผลักดันวัตถุนั้นลงในซ้ำซ้อน _array
  5. ถ้าไม่มีวัตถุอยู่ในสิ่งที่มองเห็นก็จะถือว่าเป็นวัตถุที่ไม่ซ้ำใครและผลักวัตถุนั้นเข้าไปใน Seen_array

มาสาธิตใน Code Implementation กันดีกว่า

def duplication given_array
  seen_objects = []
  duplication_objects = []

  given_array.each do |element|
    duplication_objects << element if seen_objects.include?(element)
    seen_objects << element
  end

  duplication_objects
end

ตอนนี้เรียกวิธีการทำซ้ำและผลตอบแทนการส่งออก -

dup_elements = duplication [1,2,3,4,4,5,6,6]
puts dup_elements.inspect

คำตอบที่ใช้รหัสโดยทั่วไปมักจะขมวดคิ้วบนเว็บไซต์นี้ คุณกรุณาแก้ไขคำตอบของคุณเพื่อรวมความคิดเห็นหรือคำอธิบายเกี่ยวกับรหัสของคุณได้หรือไม่? คำอธิบายควรตอบคำถามเช่น: มันทำอะไร? มันทำยังไง? มันไปไหน มันแก้ปัญหา OP อย่างไร ดู: วิธีการ Anwser ขอบคุณ!
Eduardo Baitello

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.