Hier sind zwei weitere Möglichkeiten, ein Duplikat zu finden.
Verwenden Sie ein Set
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"
Verwenden Sie select
anstelle von find
, um ein Array aller Duplikate zurückzugeben.
Verwenden 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"
Löschen .first
, um ein Array aller Duplikate zurückzugeben.
Beide Methoden werden zurückgegeben, nil
wenn keine Duplikate vorhanden sind.
Ich schlug vor,Array#difference
das dem Ruby-Kern hinzuzufügen. Weitere Informationen finden Sie in meiner Antwort hier .
Benchmark
Vergleichen wir die vorgeschlagenen Methoden. Zunächst benötigen wir ein Array zum Testen:
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
und eine Methode zum Ausführen der Benchmarks für verschiedene Testarrays:
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
Ich habe die Antwort von @ JjP nicht aufgenommen, da nur ein Duplikat zurückgegeben werden soll. Wenn seine Antwort geändert wird, entspricht dies der früheren Antwort von @ Naveed. Ich habe auch nicht die Antwort von @ Marin aufgenommen, die, obwohl sie vor der Antwort von @ Naveed veröffentlicht wurde, alle Duplikate und nicht nur eines zurückgegeben hat (ein kleiner Punkt, aber es macht keinen Sinn, beide zu bewerten, da sie identisch sind, wenn nur ein Duplikat zurückgegeben wird).
Ich habe auch andere Antworten geändert, bei denen alle Duplikate zurückgegeben wurden, um nur die zuerst gefundenen zurückzugeben. Dies sollte jedoch im Wesentlichen keine Auswirkungen auf die Leistung haben, da alle Duplikate vor der Auswahl eines Duplikats berechnet wurden.
Die Ergebnisse für jeden Benchmark sind vom schnellsten zum langsamsten aufgeführt:
Angenommen, das Array enthält 100 Elemente:
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
Betrachten Sie nun ein Array mit 10.000 Elementen:
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
Beachten Sie, dass find_a_dup_using_difference(arr)
dies viel effizienter wäre, wenn Array#difference
es in C implementiert würde, was der Fall wäre, wenn es dem Ruby-Kern hinzugefügt würde.
Fazit
Viele der Antworten sind vernünftig, aber die Verwendung eines Sets ist die eindeutig beste Wahl . Es ist am schnellsten in mittelschweren Fällen, am schnellsten in den schwierigsten Fällen und nur in rechnerisch trivialen Fällen - wenn Ihre Wahl sowieso keine Rolle spielt - kann es geschlagen werden.
Der ganz besondere Fall, in dem Sie sich für die Lösung von Chris entscheiden könnten, wäre, wenn Sie die Methode verwenden möchten, um Tausende kleiner Arrays separat zu de-duplizieren und ein Duplikat zu finden, das normalerweise weniger als 10 Elemente enthält. Dies ist etwas schneller Dies vermeidet den geringen zusätzlichen Aufwand beim Erstellen des Sets.
arr == arr.uniq
Dies wäre eine einfache und elegante Methode, um zu überprüfen, obarr
Duplikate vorhanden sind. Es werden jedoch keine Duplikate bereitgestellt.