Как uniq массив без учета регистра - PullRequest
11 голосов
/ 09 июля 2009

Насколько я знаю, результат

["a", "A"].uniq 

это

["a", "A"]

Мой вопрос:

Как мне сделать ["a", "A"]. Uniq дать мне либо ["a"], либо ["A"]

Ответы [ 7 ]

35 голосов
/ 16 октября 2013

Есть еще один способ сделать это. На самом деле вы можете передать блок uniq или uniq!, который можно использовать для оценки каждого элемента.

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

или

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

В этом случае все будет нечувствительным к регистру, поэтому он всегда будет возвращать массив ["A"]

16 голосов
/ 09 июля 2009

Просто сделайте случай согласованным первым.

например:

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

Редактировать: Если, как подсказывает mikej, возвращаемые элементы должны быть точно такими же, как и в исходном массиве, тогда это будет сделано для вас:

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

Edit2 Решение, которое должно удовлетворять mikej: -)

downcased = [] 
a.inject([]) { |result,h| 
        unless downcased.include?(h.downcase);
            result << h
            downcased << h.downcase
        end;
        result}
7 голосов
/ 09 июля 2009

вы можете построить отображение (хэш) между нормализованными по регистру (например, в нижнем регистре) значениями и фактическим значением, а затем взять только значения из хеша:

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

выбирает последнее вхождение данного слова (без учета регистра):

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

если вы хотите первое вхождение:

["a", "b", "A", "C"]\
.inject(Hash.new){ |h,element| h[element.downcase] = element  unless h[element.downcase]  ; h }\
.values
4 голосов
/ 08 октября 2013

Если вы используете ActiveSupport, вы можете использовать uniq_by . Это не влияет на случай окончательного вывода.

['A','a'].uniq_by(&:downcase) # => ['A']
4 голосов
/ 09 июля 2009
["a", "A"].map{|x| x.downcase}.uniq
=> ["a"]

или

["a", "A"].map{|x| x.upcase}.uniq
=> ["A"]
2 голосов
/ 02 октября 2009

Немного эффективнее и эффективнее использовать уникальные ключи в хешах, поэтому проверьте это:

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

вернет последний элемент, в этом случае

["A"]

при использовании || = в качестве оператора присваивания:

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

вернет первый элемент, в этом случае

["a"]

особенно для больших массивов это должно быть быстрее, так как мы не ищем массив каждый раз, используя include?

веселит ...

0 голосов
/ 09 июля 2009

Более общее решение (хотя и не самое эффективное):

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

Метод uniq_by принимает лямбду, которая проверяет равенство, и лямбду, которая возвращает хеш, и удаляет дублирующиеся объекты, как определено этими данными.

Кроме того, реализованный метод uniq_ci удаляет дубликаты строк с использованием сравнения без учета регистра.

...