如何忽略大小写对数组去重

15
据我所知,

的结果是

["a", "A"].uniq 

is

["a", "A"]

我的问题是:
如何使 ["a", "A"].uniq 返回 ["a"] 或者 ["A"]?
7个回答

43

还有另一种方法可以做到这一点。您实际上可以将一个块传递给uniquniq!,该块可用于评估每个元素。

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

在这种情况下,所有字符大小写都不敏感,因此它将始终返回数组["A"]


10
好的回答。请注意,稍微更短的变体是 ["A", "a"].uniq(&:downcase) - antinome
此解决方案将优先选择第一个重复项,即 ["A"] 而不是 ["a"]。 - vanboom

19

首先要使大小写一致。

例如:

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

编辑:如果像mikej建议的那样,返回的元素必须与原始数组中的完全相同,则以下代码将为您实现此操作:

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

编辑2 可以满足mikej的解决方案 :-)

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

虽然对于给定的示例,如果列表是["Hello", "HELLO"],那么["Hello", "HELLO"].map { |i| i.downcase }.uniq将返回["hello"],它与原始列表中的任何一个字符串都不匹配。 - mikej
编辑后的解决方案很好,除了它将使用 result.map{|i| i.downcase} 多次构建小写列表(对于原始列表中的每个元素都会执行一次),因此如果列表很大,可以将其作为单独的语句执行一次并存储在临时变量中。 - mikej
1
@Eric C的解决方案更简单。 - depquid

7

您可以建立一个映射(哈希)来将大小写规范化(例如,转为小写)的值与实际值对应起来,然后仅从哈希中获取值:

["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

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

或者

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

5
如果你正在使用ActiveSupport,你可以使用uniq_by。它不会影响最终输出的大小写。
['A','a'].uniq_by(&:downcase) # => ['A']

2

有一种更为高效的方法是在哈希表中使用唯一键,因此请查看以下内容:

["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
一个更加通用的解决方案(虽然不是最有效的):
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 方法接受一个检查相等性的 lambda 函数和一个返回哈希值的 lambda 函数,并根据这些数据定义删除重复对象。

在此基础上实现的 uniq_ci 方法使用不区分大小写的比较方式删除字符串重复项。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接