Pergunta

Tanto quanto eu sei, o resultado de

["a", "A"].uniq 

é

["a", "A"]

A minha pergunta é:

Como posso fazer [ "a", "A"]. UNIQ me dar tanto [ "a"] ou [ "A"]

Foi útil?

Solução

Apenas fazer o caso consistente em primeiro lugar.

por exemplo:

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

Editar: Se como MikeJ sugere, os elementos retornado deve ser exatamente o mesmo que na matriz original, então isso vai fazer isso para você:

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

Edit2 Solução que deve satisfazer MikeJ: -)

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

Outras dicas

Há uma outra maneira você pode fazer isso. Você pode realmente passar um bloco para uniq ou uniq! que pode ser usado para avaliar cada elemento.

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

ou

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

Neste caso, porém, tudo vai ser maiúsculas e minúsculas, por isso sempre retornará a matriz ["A"]

pode construir um mapeamento (Hash) entre os (por exemplo downcased) valores de caso-normalizadas e o valor real e, em seguida, tomar apenas os valores a partir do hash:

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

seleciona a última ocorrência de uma determinada palavra (maiúsculas e minúsculas):

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

Se você quer a primeira ocorrência:

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

ou

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

Se você estiver usando ActiveSupport, você pode usar uniq_by . Ela não afeta o caso da saída final.

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

Um pouco mais eficiente e maneira é fazer uso das teclas uniq em hashes, de modo a verificar o seguinte:

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

retornará o último elemento, neste caso

["A"]

enquanto usando || = como operador de atribuição:

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

retornará primeiro elemento, neste caso

["a"]

especialmente para grandes matrizes isso deve ser mais rápido, já que não procurar a matriz cada vez usando inclui?

aplausos ...

Uma solução mais geral (embora não o mais 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

O método uniq_by leva um lambda que verifica a igualdade, e um lambda que retorna um hash, e remove objectos duplicadas, tal como definido pelos dados.

Implementado em cima disso, o método uniq_ci remove duplicatas string usando caso comparações insensíveis.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top