2009-10-19 7 views
7

Qual è il modo più rapido/one-liner per rimuovere i duplicati in una matrice di oggetti, in base a una chiave specifica: valore o un risultato restituito da un metodo?Modo più veloce/one-liner per rimuovere i duplicati (con la chiave) in Ruby Array?

Ad esempio, ho 20 nodi Elemento XML che hanno lo stesso nome, ma hanno valori "di testo" diversi, alcuni dei quali sono duplicati. Vorrei rimuovere i duplicati dicendo "if element.text == previous_element.text, rimuovilo". Come faccio a farlo in Ruby nella quantità minima di codice?

Ho visto come farlo per semplici valori stringa/intero, ma non per oggetti.

+0

Vedere la mia risposta per una versione moderna. –

risposta

14

Ecco il modo standard di hashing. Notare l'uso dell'operatore ||=, che è un modo più conveniente (a ||= b) per scrivere a = b unless a.

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

È possibile farlo in una sola riga.

Lo script richiede i controlli di uguaglianza O (n) delle stringhe text. Questo è ciò che è coperto da O (n) quando vedi un hash.

+0

Non esattamente il più veloce, poiché viene eseguito in tempo O (n^2). Quindi, di nuovo, non è molto importante visto quanto tempo di CPU è economico. – EmFi

+1

@EmFi, l'accesso alla tabella hash non richiede O (n) (dovremmo iterare la stringa 'text', ma dovremo farlo comunque). Ho appena pubblicato una risposta su questo argomento: http://stackoverflow.com/questions/1590405/distinguishing-extra-element-from-two-arrays/1590536#1590536 –

+0

@Pavel Ci dispiace, hai ragione. Mi sono confuso per un secondo nel pensare che la chiamata ai valori aggiunti l'abbia resa O (n^2). Quando lo rende solo O (2n). – EmFi

10

Questo fa tutto:

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

breve? Sì.

(l'asterisco è facoltativo; sembra necessario per 1.8.6).

Ad esempio:

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 parte: ecco la piccola classe di test che ho usato:

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

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

questo è davvero bello, che cos'è (e: ultimo)? –

+0

è andato nella nuova versione, ancora più breve, più semplice :). Tuttavia, dicendo 'ary.map {| x | x.last} 'e' ary.map (&: last) 'sono equivalenti. – Peter

+0

Ho il seguente errore: in '[] ': numero dispari di argomenti per Hash (ArgumentError) –

4

Usa Array#uniq con un blocco. Nel tuo caso:

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

Questo è stato introdotto in Ruby 1.9.2, quindi se si utilizza una versione precedente, è possibile utilizzare backports con require 'backports/1.9.2/array/uniq'

Problemi correlati