Dodaj element do tablicy, jeśli jeszcze go tam nie ma

92

Mam klasę Ruby

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 = []

Chcę pchnąć MyClass#item1do unique_array_of_item1, ale tylko jeśli unique_array_of_item1nie zawierają że item1jeszcze. Znam proste rozwiązanie: po prostu iteruj my_arrayi sprawdź, czy unique_array_of_item1już zawiera bieżący, item1czy nie.

Czy jest jakieś wydajniejsze rozwiązanie?

Alan Coromano
źródło

Odpowiedzi:

82

Możesz użyć Set zamiast Array.

Jiří Pospíšil
źródło
Chociaż prawdą jest, że dokumentacja mówi, że zestawy nie są uporządkowane, w rzeczywistości są (od Ruby 1.9) uporządkowane. Jeśli spojrzysz na kod, główne metody, których użyjesz do uzyskania kolejności (takie jak Set#eachi Set#to_a) delegowane @hash. A od Ruby 1.9 Hashe są zamawiane. „Hashe wyliczają ich wartości w kolejności, w jakiej zostały wstawione odpowiednie klucze”. ruby-doc.org/core-1.9.1/Hash.html
phylae
Nigdy nie było czegoś takiego jak zestaw. Są niesamowici, dziękuję bardzo
Brad
123

@Coorasse ma dobrą odpowiedź , chociaż powinna to być:

my_array | [item]

I zaktualizować my_arrayna miejscu:

my_array |= [item]
Jason Denney
źródło
63
lub my_array |= [item]który zostanie zaktualizowany my_arrayna miejscu
andorov
2
Może czegoś tu brakuje, ale operator | = wydaje mi się nie działać? Używam Ruby 2.1.1
Viet,
@Viet |=działa dobrze w moich testach z 2.1.1. Opisz swój przypadek testowy lub otwórz nowe pytanie.
zniknie
Testuję to ponownie i teraz działa. Nie wiem, co robiłem wcześniej, odkąd mój komentarz pojawił się wiele miesięcy temu.
Viet,
1
Jaka jest złożoność tego?
Nobita,
41

Nie musisz wykonywać iteracji my_arrayręcznie.

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

Edytować:

Jak zauważa Tombart w swoim komentarzu, używanie Array#include?nie jest zbyt wydajne. Powiedziałbym, że wpływ na wydajność jest pomijalny w przypadku małych tablic, ale możesz chcieć wybrać Setwiększe.

doesterr
źródło
6
zdecydowanie nie chcesz tego robić! array.include?(item)ma złożoność O(n)- więc przypomina iterację całej tablicy. spójrz na ten test porównawczy: gist.github.com/deric/4953652
Tombart,
32

Możesz przekonwertować item1 na tablicę i dołączyć do nich:

my_array | [item1]
coorasse
źródło
1
To powinno być |nie ||(patrz odpowiedź Jasona)
Seth
1
Moja wina. Przepraszam. Zredagowano odpowiedź
coorasse
3

Należy pamiętać, że klasa Set i | Metoda (zwana także „Set Union”) zwróci szereg unikalnych elementów, co jest świetne, jeśli nie chcesz mieć duplikatów, ale będzie nieprzyjemną niespodzianką, jeśli masz nieunikalne elementy w swojej oryginalnej tablicy zgodnie z projektem.

Jeśli masz co najmniej jeden zduplikowany element w oryginalnej tablicy, którego nie chcesz stracić, iterowanie po tablicy z wczesnym zwrotem jest najgorszym przypadkiem O (n), co nie jest takie złe w ogólnym schemacie .

class Array
  def add_if_unique element
    return self if include? element
    push element
  end
end
elreimundo
źródło
0

Nie jestem pewien, czy to idealne rozwiązanie, ale zadziałało:

    host_group = Array.new if not host_group.kind_of?(Array)
    host_group.push(host)
witkacy26
źródło