2017-01-24 3 views
4

Wie kann ich effektiv löschen negative Duplikate von positiven ganzen Zahlen aus einem Array von positiven und negativen ganzen Zahlen wie folgt: [1, 5, 10, 5, -5, -1, 9] als Ergebnis möchte ich haben : [1, 5, 10, 5, 9] (-1 und -5 entfernt, wie sie negative Duplikate von 1 und 5)Wie lösche ich negative Duplikate aus einem Array?

Antwort

6

Dies ist die einfachste Methode, die ich finden konnte:

  • positive Zahlen
  • wählen ihre Gegenspieler
  • berechnen sie aus dem ursprünglichen Array entfernen

array = [1, 5, 10, 5, -5, -1, 9] 

p array - array.select{ |i| i > 0 }.map{ |i| -i } 
# [1, 5, 10, 5, 9] 

Es verwendet Array#-, die ziemlich schnell sein sollte.

+0

Cool! Sie können dies sogar als 'map (: - @)' verkürzen, obwohl ich nicht sicher bin, wie viele Leute den richtigen Namen der Negationsmethode kennen :) – akuhn

+0

Das ist, wozu rubocop es konvertiert hat, aber Sie haben Recht, vielleicht auch nicht sehr gut lesbar sein. Um ehrlich zu sein, ich habe es noch nie gesehen oder benutzt. Weißt du, warum '@' verwendet wird? –

+0

Um das unäre '-a' vom binären' a-b'-Operator zu unterscheiden, ist nicht sicher, warum ein '@' Symbol verwendet wird. Ich glaube, Matz hat gerade etwas erfunden. Das gleiche gilt für '+ @' und '~ @', aber nicht für '!', Was mich immer verwirrt. – akuhn

0

In Ruby könnte man die uniq Methode: https://ruby-doc.org/core-2.2.0/Array.html#method-i-uniq.

Andernfalls sollten Sie eine Antwort wie this one überprüfen, die beschreibt, wie Sie eine Struktur erstellen, die durch ein Array iteriert.

+0

Wie würden Sie "uniq" auf das in der Frage angegebene Beispiel anwenden? –

+0

CJ, am besten geben Sie ein Codebeispiel mit Ihrer Antwort. Es empfiehlt sich, Stakcoverflow zusätzlich zu den Links zur Dokumentation Beispiele bereitzustellen. – akuhn

+0

Duplikate für positive Ganzzahlen sind erlaubt, also funktioniert das nicht. – Anthony

4

Sie diese einfach ablehnen mit Array # tun können:

>> a = [1, 5, 10, 5, -5, -1, 9] 
>> a.reject { |e| e < 0 && a.include?(e.abs) } 
=> [1, 5, 10, 5, 9] 

mit einem anderen Beispiel Um zu klären, wird dies keine negativen Werte entfernen, die keine entsprechenden positiven Wert im Array:

>> b = [1, 5, 10, 5, -5, -1, 9, -15] 
>> b.reject { |e| e < 0 && b.include?(e.abs) } 
=> [1, 5, 10, 5, 9, -15] 

Sie können eine Methode, wie so definieren:

def reject_negative_duplicates(array) 
    array.reject { |e| e < 0 && array.include?(e.abs) } 
end 

>> reject_negative_duplicates(a) 
=> [1, 5, 10, 5, 9] 
>> reject_negative_duplicates(b) 
=> [1, 5, 10, 5, 9, -15] 

oder erweitern (Affe Patch) Array :

class Array 
    def reject_negative_duplicates 
    self.reject { |e| e < 0 && self.include?(e.abs) } 
    end 
end 

>> a.reject_negative_duplicates 
=> [1, 5, 10, 5, 9] 
>> b.reject_negative_duplicates 
=> [1, 5, 10, 5, 9, -15] 
+2

umgewandelt Sehr lesbare Lösung. Es ist O (n^2), also könntest du es beschleunigen, indem du das Original-Array in ein 'Set' für die 'include'-Lookups legst, um O (1) zu sein. – Anthony

+0

Guter Punkt. Für ein großes Array wäre das sinnvoll. – moveson

+0

'Selbst.'wird nicht in der Definition der' Array' Methode benötigt, aber ich weiß, dass einige Leute es trotzdem hinzufügen wollen. –

5

Sie können dies mit nur zwei Durchgängen durch das Array in O(n) tun, indem sie die positiven Zahlen dann aus den Array negativen Werten, deren abs gehasht wurde die Ablehnung Hashing:

interessant
def reject_neg_dups(arr) 
    positives = Hash[arr.map {|x| (x>0) ? [x,1] : nil }.compact] 
    arr.reject { |x| (x < 0) && positives[-x] } 
end 

reject_neg_dups([-1, 1, 2, -2]) # => [1, 2] 
reject_neg_dups([-1, 1, -2]) # => [1, -2] since 2 does not appear 

Beachten Sie, dass the Array- solutions sind wesentlich schneller als andere aufgeführt bisher:

require 'benchmark' 

def reject_neg_dups_hash(arr) 
    positives = Hash[arr.map {|x| (x>0) ? [x,1] : nil }.compact] 
    arr.reject { |x| (x < 0) && positives[-x] } 
end 

def reject_neg_dups_include(arr) 
    arr.reject { |x| (x < 0) && arr.include?(x.abs) } 
end 

def reject_neg_dups_arrayminus(arr) 
    arr - arr.select { |i| i > 0 }.map { |i| -i } 
end 

def reject_neg_dups_arrayminusewo(arr) 
    arr - arr.each_with_object([]) { |n,b| b << -n if n > 0 } 
end 

arr = Array.new(1000) { rand(-100..100) } 
N = 1000 
Benchmark.bm(15) do |x| 
    x.report('Array-') { N.times { reject_neg_dups_arrayminus(arr.dup) } } 
    x.report('Array-ewo') { N.times { reject_neg_dups_arrayminusewo(arr.dup) } } 
    x.report('hash')  { N.times { reject_neg_dups_hash(arr.dup) } } 
    x.report('include?') { N.times { reject_neg_dups_include(arr.dup) } } 
end 

Beispiel Ausgang:

     user  system  total  real 
Array-   0.180000 0.000000 0.180000 ( 0.187512) 
Array-ewo   0.200000 0.000000 0.200000 ( 0.194663) 
hash    0.250000 0.010000 0.260000 ( 0.253355) 
include?   3.660000 0.000000 3.660000 ( 3.666313) 
+1

'Array # -' erstellt auch einen Hash, tut dies aber intern im nativen Code, daher ist es um einen konstanten Faktor schneller, hat aber wahrscheinlich die gleiche' O (n) '-Komplexität. – akuhn

+0

@akuhn guter Punkt, eingebaute sind fast immer schneller als interpretierte Code, auch mit möglicherweise gleichwertigen Big-Oh-Leistung. – maerics

+0

Da ich eine Variante von 'Array-' war, erwartete ich, dass meine Lösung eine ähnliche Leistung haben würde. Es ist ungefähr 12% schneller mit Ihrem Benchmark. –

3
arr = [1, 5, 10, 0, 5, -5, -1, 9, -4] 

arr - arr.each_with_object([]) { |n,b| b << -n if n > 0 } 
    #=> [1, 5, 10, 0, 5, 9, -4] 
+0

Ich sehe das ist aber eine kleine Variante von @ Erics Lösung. –

Verwandte Themen