2017-07-11 2 views
0

Ich habe eine einfache Methode, die durch ein Array iteriert und ein Duplikat zurückgibt. (Oder Duplikate)Wie konvertiert man eine dreizeilige Ruby-Methode in eine

def find_dup(array) 
    duplicate = 0 
    array.each { |element| duplicate = element if array.count(element) > 1} 
    duplicate 
end 

Es funktioniert, aber ich möchte diese elegant mehr auszudrücken.

Der Grund ist es drei Linien ist, dass die Variable „Duplikat“, die die Methode muss, kehren nicht sichtbar für das Verfahren ist, wenn ich es innerhalb des Blocks einführen, dh

def find_dup(array) 
    array.each { |element| duplicate = element if array.count(element) > 1} 
    duplicate 
end 

Ich habe habe ein paar Möglichkeiten ausprobiert, um "duplicate" als Ergebnis eines Blocks zu definieren, aber ohne Erfolg.

Irgendwelche Gedanken?

+0

Sind Sie versuchen, eine einzelne Kopie oder das letzte Duplikat oder ein Array von Dubletten zu finden? Außerdem durchläuft Ihr aktueller Code alle Elemente im Array zweimal in verschachtelter Weise und setzt das Duplikat auf das zuletzt gefundene Duplikat zurück. –

Antwort

1

Es ist ein bisschen zu viel, um sauber in einem Einzeiler zu tun, aber das ist eine effiziente Lösung.

def find_dups(arr) 
    counts = Hash.new { |hash,key| hash[key] = 0 } 
    arr.each_with_object(counts) do |x, memo| 
    memo[x] += 1 
    end.select { |key,val| val > 1 }.keys 
end 

Der Hash.new Anruf instanziiert ein Hash, wo der Standardwert ist 0.

each_with_object diesen Hash modifiziert in arr die Zählung jedes Elements zu verfolgen, dann an der die filter Ende wird nur verwendet, auszuwählen, diejenigen mit einer Zählung größer als eins.

Der Vorteil dieses Ansatzes gegenüber einer Lösung mit Array#includes? oder Array#count besteht darin, dass das Array nur einmal gescannt wird. Daher ist es eine O (N) Zeit anstelle von O (N^2).

+0

Danke, das ist eine bessere Lösung. Der One-Liner entgeht mir immer noch, aber, hey, es war einen Versuch wert! – clockworkpc

1

Ihre Methode findet nur das letzte Duplikat im Array. Wenn Sie alle Duplikate möchten, würde ich etwas tun wie folgt:

def find_dups(arr) 
    dups = Hash.new { |h, k| h[k] = 0 } 
    arr.each { |el| dups[el] += 1 } 
    dups.select { |k, v| v > 1 }.keys 
end 
+0

Danke, dass Sie darauf hingewiesen haben. Es beantwortet meine Frage nicht genau, aber es ist gut, über den Code Bescheid zu wissen. – clockworkpc

+0

Also ist das Problem, dass der Block eine Schließung sein soll, so dass 'duplicate' immer noch verfügbar ist, nachdem der Block ausgeführt wird? Denn wenn ich das sehe, denke ich, dass Sie es so machen. –

0

Sie können dies als eine Zeile tun und es fließt ein bisschen schöner. Obwohl dies die erste Instanz eines Duplikats finden würde, während Ihr Code die letzte Instanz eines Duplikats zurückgibt, ist nicht sicher, ob dies zu Ihrer Anforderung gehört.

def find_dup(array) 
    array.group_by { |value| value }.find { |_, groups| groups.count > 1 }.first 
end 

Beachten Sie auch, dass die Dinge eine Zeile zu machen, bedeutet nicht unbedingt besser ist. Ich würde den Code besser lesbar über mehrere Zeilen verteilt finden, aber das ist nur meine Meinung.

def find_dup(array) 
    array.group_by { |value| 
    value 
    }.find { |_, groups| 
    groups.count > 1 
    }.first 
end 
1

Wenn das, was Sie wirklich wollen, ist ein Einzeiler, die nicht mit Big-O Komplexität angeht und liefert nur die letzte Duplikat in dem Array, würde ich dies tun:

def find_last_dup(arr) 
    arr.reverse_each { |el| return el if arr.count(el) > 1 } 
end 
+0

Verwenden Sie 'reverse_each', wenn Sie' reverse' und 'each' verketten. Ansonsten eine nette Lösung. –

0

Gegeben :

> a 
=> [8, 5, 6, 6, 5, 8, 6, 1, 9, 7, 2, 10, 7, 7, 3, 4] 

Sie können Gruppe die dups zusammen:

> a.uniq.each_with_object(Hash.new(0)) {|e, h| c=a.count(e); h[e]=c if c>1} 
=> {8=>2, 5=>2, 6=>3, 7=>3} 

Oder

> a.group_by{ |e| e}.select{|k,v| v if v.length>1} 
=> {8=>[8, 8], 5=>[5, 5], 6=>[6, 6, 6], 7=>[7, 7, 7]} 

In jedem Fall wird die Reihenfolge des Ergebnisses auf der Reihenfolge der Elemente in a basiert, die dups haben.Wenn Sie nur die erste:

> a.group_by{ |e| e}.select{|k,v| v if v.length>1}.first 
=> [8, [8, 8]] 

Oder zuletzt:

> a.group_by{ |e| e}.select{|k,v| v if v.length>1}.to_a.last 
=> [7, [7, 7, 7]] 

Wenn Sie wollen ‚schneller Vorlauf‘ auf den ersten Wert, der eine dup hat, können Sie drop_while:

> b=[1,2,3,4,5,4,5,6] 
> b.drop_while {|e| b.count(e)==1 }[0] 
=> 4 

Oder das letzte:

0

Ich will nur um einen weiteren Ansatz für die Mischung hinzuzufügen.

Alternativ können Sie lineare Zeit Komplexität in zwei Zeilen erhalten.

def find_last_dup(arr) 
    freq = arr.each_with_object(Hash.new(0)) { |x, obj| obj[x] += 1 } 
    arr.reverse_each.detect { |x| freq[x] > 1 } 
end 

Aus Gründen der Argumentation, kann dieser Ansatz auch auf eine Zeile reduziert werden, aber dies würde unidiomatic und verwirrend sein.

def find_last_dup(arr) 
    arr.each_with_object(Hash.new(0)) { |x, obj| obj[x] += 1 } 
    .tap do |freq| return arr.reverse_each.detect { |x| freq[x] > 1 } end 
end 
0
def find_duplicates(array) 
    array.dup.uniq.each { |element| array.delete_at(array.index(element)) }.uniq 
end 

Das obige Verfahren find_duplicates dem Eingangsfeld dupliziert und löscht das erste Vorkommen aller Elemente, mit nur Rest Vorkommen der doppelten Elemente des Arrays zu verlassen.

Beispiel:

array = [1, 2, 3, 4, 3, 4, 3] 
=> [1, 2, 3, 4, 3, 4, 3] 
find_duplicates(array) 
=> [3, 4] 
Verwandte Themen