2009-07-09 8 views

Respuesta

16

Simplemente haz que el caso sea consistente primero.

por ejemplo:

["a","A"].map{|i| i.downcase}.uniq 

Editar: Si como mikej sugiere, los elementos devueltos debe ser exactamente el mismo que en la matriz original, entonces esto va a hacer eso por usted:

a.inject([]) { |result,h| result << h unless result.map{|i| i.downcase}.include?(h.downcase); result } 

Edit2 Solución que debería satisfacer mikej :-)

downcased = [] 
a.inject([]) { |result,h| 
     unless downcased.include?(h.downcase); 
      result << h 
      downcased << h.downcase 
     end; 
     result} 
+0

Mientras que esto funcionaría para el ejemplo dado si la lista era algo así como ["Hola", "HOLA"] luego ["Hola", "HOLA"]. Map {| i | i.downcase} .uniq devolvería ["hello"] que no coincide con ninguna de las cadenas en la lista original. – mikej

+0

La solución editada es válida, excepto que construirá la lista reducida usando result.map {| i | i.downcase} varias veces (una para cada elemento en la lista original) así que tal vez ejecute eso una vez como una declaración separada y almacene en una variable temporal si la lista es grande. – mikej

+1

La solución de @Eric C es mucho más simple. – depquid

4
["a", "A"].map{|x| x.downcase}.uniq 
=> ["a"] 

o

["a", "A"].map{|x| x.upcase}.uniq 
=> ["A"] 
+0

¡Ack! ¡Golpeado! – Codebeef

0

Una solución más general (aunque no el más eficiente):

class EqualityWrapper 
    attr_reader :obj 

    def initialize(obj, eq, hash) 
    @obj = obj 
    @eq = eq 
    @hash = hash 
    end 

    def ==(other) 
    @eq[@obj, other.obj] 
    end 

    alias :eql? :== 

    def hash 
    @hash[@obj] 
    end 
end 

class Array 
    def uniq_by(eq, hash = lambda{|x| 0 }) 
    map {|x| EqualityWrapper.new(x, eq, hash) }. 
    uniq. 
    map {|x| x.obj } 
    end 

    def uniq_ci 
    eq = lambda{|x, y| x.casecmp(y) == 0 } 
    hash = lambda{|x| x.downcase.hash } 
    uniq_by(eq, hash) 
    end 
end 

El método uniq_by toma un lambda que verifica la igualdad, y un lambda que devuelve una hash, y elimina los objetos duplicados según lo definido por esos datos.

Implementado además de eso, el método uniq_ci elimina los duplicados de cadenas mediante comparaciones insensibles a mayúsculas y minúsculas.

7

puede crear un mapeo (hash) entre la caja normalizada (p. Ej.downcased) los valores y el valor real y luego tomar sólo los valores de hash:

["a", "b", "A", "C"]\ 
.inject(Hash.new){ |h,element| h[element.downcase] = element ; h }\ 
.values 

selecciona la última ocurrencia de una palabra dada (mayúsculas y minúsculas):

["A", "b", "C"] 

si desea que la primera aparición :

["a", "b", "A", "C"]\ 
.inject(Hash.new){ |h,element| h[element.downcase] = element unless h[element.downcase] ; h }\ 
.values 
+0

+1 Muy inteligente. – DanSingerman

2

un poco más eficiente y forma es hacer uso de las teclas uniq en hashes, así que mira esto:

["a", "A"].inject(Hash.new){ |hash,j| hash[j.upcase] = j; hash}.values 

devolverá el último elemento, en este caso

["A"] 

mientras que usando || = operador como Asignar:

["a", "A"].inject(Hash.new){ |hash,j| hash[j.upcase] ||= j; hash}.values 

volverá primer elemento, en este caso

["a"] 

especialmente para matrices grandes esto debería ser más rápido que no buscamos en la matriz cada vez que se incluye ?

aplausos ...

3

Si está utilizando ActiveSupport, puede utilizar uniq_by. No afecta el caso de la salida final.

['A','a'].uniq_by(&:downcase) # => ['A'] 
29

Hay otra manera de hacerlo. En realidad, puede pasar un bloque a uniq o uniq! que se puede usar para evaluar cada elemento.

["A", "a"].uniq { |elem| elem.downcase } #=> ["A"] 

o

["A", "a"].uniq { |elem| elem.upcase } #=> ["A"] 

En este caso, sin embargo, todo estará entre mayúsculas y minúsculas por lo que siempre devolverá la matriz ["A"]

+6

Gran respuesta. Tenga en cuenta que una variación ligeramente más corta es '[" A "," a "]. Uniq (&: downcase)'. – antinome

+0

@antinome Mucho más legible. –

+0

Esta debería ser claramente la respuesta aceptada ... –