2009-10-19 49 views
7

¿Cuál es la forma más rápida/de una línea para eliminar duplicados en una matriz de objetos, en función de una clave específica: valor, o un resultado devuelto por un método?Forma más rápida/de una línea para eliminar duplicados (por clave) en Ruby Array?

Por ejemplo, tengo 20 nodos XML Element que tienen todos el mismo nombre, pero tienen diferentes valores de "texto", algunos de los cuales son duplicados. Me gustaría eliminar los duplicados diciendo "if element.text == previous_element.text, eliminarlo". ¿Cómo hago eso en Ruby en la cantidad más corta de código?

He visto cómo hacerlo para valores simples de cadena/entero, pero no para objetos.

+0

Véase mi respuesta para una versión moderna. –

Respuesta

14

Aquí está la manera estándar de hashy. Tenga en cuenta el uso del operador ||=, que es una manera más conveniente (a ||= b) de escribir a = b unless a.

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

Puede hacerlo en una sola línea.

La secuencia de comandos necesita O (n) verificaciones de igualdad de text cadenas. Eso es lo que cubre O (n) cuando ve un hash.

+0

No es exactamente el más rápido, ya que se ejecuta en O (n^2) tiempo. Por otra parte, no es realmente importante dado lo barato que es ahora el tiempo de la CPU. – EmFi

+1

@EmFi, acceder a la tabla hash no toma O (n) (debemos iterar la cadena 'text', pero tendremos que hacerlo de todos modos). Acabo de publicar una respuesta sobre este asunto: http://stackoverflow.com/questions/1590405/distinguishing-extra-element-from-two-arrays/1590536#1590536 –

+0

@Pavel Lo sentimos, tienes razón. Me confundí por un segundo al pensar que la llamada de valores agregados lo hizo O (n^2). Cuando solo lo hace O (2n). – EmFi

10

Esto lo hace todo:

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

corto? Sí.

(el asterisco es opcional, parece ser necesario para 1.8.6).

Por ejemplo:

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

parte aburrida: aquí está la clase de prueba pequeño solía:

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

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

esto es genial, ¿qué es eso (&: último)? –

+0

se ha ido en la versión nueva, incluso más corta, más simple :). Sin embargo, diciendo 'ary.map {| x | x.last} 'y' ary.map (&: last) 'son equivalentes. – Peter

+0

Tengo el siguiente error: en '[] ': número impar de argumentos para Hash (ArgumentError) –

4

Uso Array#uniq con un bloque. En su caso:

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

Esto se introdujo en Ruby 1.9.2, por lo que si se utiliza una versión anterior, puede utilizar backports con require 'backports/1.9.2/array/uniq'

Cuestiones relacionadas