Fügen Sie einem Array ein Element hinzu, falls es noch nicht vorhanden ist

92

Ich habe eine Ruby-Klasse

class MyClass
  attr_writer :item1, :item2
end

my_array = get_array_of_my_class() #my_array is an array of MyClass
unique_array_of_item1 = []

Ich möchte drücken MyClass#item1zu unique_array_of_item1, aber nur , wenn unique_array_of_item1nicht das enthält item1noch. Es gibt eine einfache Lösung, die ich kenne: Durchlaufen Sie einfach my_arrayund prüfen Sie, ob unique_array_of_item1der aktuelle bereits vorhanden ist item1oder nicht.

Gibt es eine effizientere Lösung?

Alan Coromano
quelle

Antworten:

82

Sie können Set anstelle von Array verwenden.

Jiří Pospíšil
quelle
Zwar sagen die Dokumente, dass Sets nicht geordnet sind, aber sie sind tatsächlich (ab Ruby 1.9) geordnet. Wenn Sie sich den Code ansehen, werden die Hauptmethoden verwendet, mit denen Sie die Bestellung abrufen (z. B. Set#eachund Set#to_a) @hash. Und ab Ruby 1.9 werden Hashes bestellt. "Hashes zählen ihre Werte in der Reihenfolge auf, in der die entsprechenden Schlüssel eingefügt wurden." ruby-doc.org/core-1.9.1/Hash.html
Phylae
Nie neu gab es so etwas wie ein Set. Sie sind fantastisch, vielen Dank
Brad
121

@Coorasse hat eine gute Antwort , obwohl es sein sollte:

my_array | [item]

Und um my_arrayan Ort und Stelle zu aktualisieren :

my_array |= [item]
Jason Denney
quelle
63
oder my_array |= [item]welche wird my_arrayan Ort und Stelle aktualisiert
andorov
2
Vielleicht fehlt mir hier etwas, aber der Operator | = scheint bei mir nicht zu funktionieren? Ich verwende Ruby 2.1.1
Viet
@Viet |=funktioniert in meinen Tests mit 2.1.1 einwandfrei . Bitte beschreiben Sie Ihren Testfall oder öffnen Sie eine neue Frage.
Depquid
Testen Sie es erneut und es funktioniert jetzt. Ich weiß nicht, was ich früher gemacht habe, da mein Kommentar vor vielen Monaten abgegeben wurde.
Viet
1
Was ist die Komplexität davon?
Nobita
40

Sie müssen nicht my_arrayvon Hand durchlaufen .

my_array.push(item1) unless my_array.include?(item1)

Bearbeiten:

Wie Tombart in seinem Kommentar betont, ist die Verwendung Array#include?nicht sehr effizient. Ich würde sagen, dass die Auswirkungen auf die Leistung bei kleinen Arrays vernachlässigbar sind, aber Sie sollten sich Setfür größere Arrays entscheiden .

doesterr
quelle
5
das willst du definitiv nicht! array.include?(item)hat Komplexität O(n)- es ist also so, als würde man das gesamte Array iterieren. Schauen Sie sich diesen Benchmark an: gist.github.com/deric/4953652
Tombart
32

Sie können item1 in ein Array konvertieren und diese verbinden:

my_array | [item1]
Coorasse
quelle
1
Dies sollte |nicht sein ||(siehe Jasons Antwort)
Seth
1
Mein Fehler. Es tut uns leid. Bearbeitet die Antwort
Coorasse
3

Es ist wichtig zu beachten, dass die Set-Klasse und die | Die Methode (auch "Set Union" genannt) liefert ein Array eindeutiger Elemente. Dies ist ideal, wenn Sie keine Duplikate möchten. Dies ist jedoch eine unangenehme Überraschung, wenn Sie nicht eindeutige Elemente in Ihrem ursprünglichen Array haben.

Wenn Sie mindestens ein doppeltes Element in Ihrem ursprünglichen Array haben, das Sie nicht verlieren möchten, ist das Durchlaufen des Arrays mit einer frühen Rückgabe O (n) im schlimmsten Fall, was im großen Schema der Dinge nicht allzu schlecht ist .

class Array
  def add_if_unique element
    return self if include? element
    push element
  end
end
elreimundo
quelle
0

Ich bin mir nicht sicher, ob es die perfekte Lösung ist, habe aber für mich gearbeitet:

    host_group = Array.new if not host_group.kind_of?(Array)
    host_group.push(host)
witkacy26
quelle