Burada bir kopya bulmanın iki yolu daha var.
Bir set kullanın
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"
Tüm kopyaların bir dizisini döndürmek için selectyerine kullanın find.
kullanım 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"
.firstTüm kopyaların bir dizisini döndürmek için bırakın .
nilHiçbir kopya yoksa her iki yöntem de geri döner .
Bunu Ruby çekirdeğine eklenmesini önerdimArray#difference . Cevabımda daha fazla bilgi var .
Karşılaştırma
Önerilen yöntemleri karşılaştıralım. İlk olarak, test için bir diziye ihtiyacımız var:
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
ve farklı test dizileri için karşılaştırmaları çalıştırma yöntemi:
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'nin cevabını dahil etmedim çünkü sadece bir kopya geri döndürülecek ve cevabı bunun için değiştirildiği zaman @ Naveed'in önceki cevabı ile aynı. Ayrıca @ Naveed'in cevabından önce yayınlanırken, sadece bir kopyadan ziyade tüm kopyaları döndüren @ Marin cevabını dahil etmedim (küçük bir nokta, ancak sadece bir kopyayı döndürdüğünde aynı oldukları için her ikisini de değerlendirmenin bir anlamı yok).
Ayrıca tüm kopyaları sadece ilk bulunanı döndürmek için döndüren diğer cevapları değiştirdim, ancak bir tane seçmeden önce tüm kopyaları hesapladıkları için performans üzerinde hiçbir etkisi olmamalıdır.
Her kıyaslama ölçütü için sonuçlar en hızlıdan en yavaşa doğru listelenir:
İlk önce dizinin 100 öğe içerdiğini varsayalım:
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
Şimdi 10.000 öğeden oluşan bir dizi düşünün:
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
Not find_a_dup_using_difference(arr)ise çok daha verimli olacaktır Array#differenceBu Ruby çekirdeğe eklenmesi durumunda durum olurdu, C uygulanmıştır.
Sonuç
Cevapların çoğu makul ancak bir Set kullanmak en iyi seçimdir . Orta-sert durumlarda en hızlı, en zor ve en hesaplı önemsiz durumlarda en hızlı eklem - seçiminiz hiçbir zaman önemli olmayacaksa - yenilebilir.
Chris'in çözümünü seçebileceğiniz çok özel bir durum, binlerce küçük diziyi ayrı olarak çoğaltmak ve genellikle 10 öğeden daha az bir kopya bulmayı beklemek istiyorsanız yöntemi kullanmak isteyeceksiniz. Bu biraz daha hızlı olacak Set oluşturmanın ek ek yükünü ortadan kaldırır.
arr == arr.uniqarryinelemelerin olup olmadığını kontrol etmenin kolay ve zarif bir yolu olabilir , ancak yinelenenleri sağlamaz.