2009-10-19 7 views
7

Jaki jest najszybszy/jednolinijkowy sposób usuwania duplikatów z tablicy obiektów na podstawie określonego klucza: wartości lub wyniku zwróconego przez metodę?Najszybszy/jednolinijkowy sposób na usuwanie duplikatów (według klucza) w Ruby Array?

Na przykład, mam 20 węzłów Elementu XML, które są identyczne, ale mają różne wartości "tekstowe", z których niektóre są duplikatami. Chciałbym usunąć duplikaty, mówiąc "if element.text == previous_element.text, remove it". Jak to zrobić w Ruby w jak najkrótszej ilości kodu?

Widziałem, jak to zrobić dla prostych wartości ciąg/liczba całkowita, ale nie dla obiektów.

+0

Zobacz moją odpowiedź na nowoczesne podejście. –

Odpowiedz

14

Oto standardowy sposób hashy. Zwróć uwagę na użycie operatora ||=, co jest wygodniejszym sposobem (a ||= b) napisania a = b unless a.

array.inject({}) do |hash,item| 
    hash[item.text]||=item 
    hash 
end.values.inspect 

Możesz to zrobić również w jednej linii.

Ten skrypt wymaga sprawdzenia równości O (n) w ciągach text. To jest omówione w punkcie O (n), gdy zobaczysz skrót.

+0

Niezupełnie najszybszy, ponieważ działa w czasie O (n^2). To znowu nie jest ważne, biorąc pod uwagę, jak tania jest teraz czas procesora. – EmFi

+1

@EmFi, dostęp do tablicy mieszającej nie zajmuje O (n) (powinniśmy przetestować ciąg 'text', ale i tak będziemy musieli to zrobić). Właśnie napisałem odpowiedź na ten temat: http://stackoverflow.com/questions/1590405/distinguishing-extra-element-from-two-arrays/1590536#1590536 –

+0

@Pavel Przepraszam, masz rację. Przez chwilę wpadłem w zakłopotanie myśląc, że dodanie wartości dodanej spowodowało, że O (n^2). Kiedy po prostu sprawia, że ​​jest O (2n). – EmFi

10

to robi to wszystko:

Hash[*a.map{|x| [x.text, x]}].values 

krótki? tak.

(gwiazdka jest opcjonalna, wydaje się być wymagana dla wersji 1.8.6).

Na przykład:

a = [Thing.new('a'), Thing.new('b'), Thing.new('c'), Thing.new('c')] 
=> [#<Thing a>, #<Thing b>, #<Thing c>, #<Thing c>] 

Hash[a.map{|x| [x.text, x]}].values 
=> [#<Thing a>, #<Thing b>, #<Thing c>] 

Boring część: oto mała klasa testu użyłem:

class Thing 
    attr_reader :text 
    def initialize(text) 
    @text = text 
    end 

    def inspect 
    "#<Thing #{text}>" 
    end 
end 
+0

to jest naprawdę fajne, co to jest (i: ostatnie)? –

+0

zniknęło w nowej, jeszcze krótszej, prostszej wersji :). Jednak mówiąc: "ary.map {| x | x.last} 'i' ary.map (&: last) 'są równoważne. – Peter

+0

Mam następujący błąd: w '[] ': nieparzysta liczba argumentów dla Hash (ArgumentError) –

4

Zastosowanie Array#uniq z blokiem. W twoim przypadku:

array.uniq(&:text) # => array with duplicated `text` removed 

ten został wprowadzony w Ruby 1.9.2, więc jeśli używasz starszej wersji, można użyć backports z require 'backports/1.9.2/array/uniq'