如何选择独特的元素

我想用uniq_elements方法扩展Array类,该方法返回多重性为1的元素。 我也uniq一样使用闭包到我的新方法。 例如:

 t=[1,2,2,3,4,4,5,6,7,7,8,9,9,9] t.uniq_elements # => [1,3,5,6,8] 

关闭示例:

 t=[1.0, 1.1, 2.0, 3.0, 3.4, 4.0, 4.2, 5.1, 5.7, 6.1, 6.2] t.uniq_elements{|z| z.round} # => [2.0, 5.1] 

tt.uniqt.to_set-t.uniq.to_settt.uniq 。 我不关心速度,我在程序中只调用一次,所以它可能很慢。

辅助方法

此方法使用帮助程序:

 class Array def difference(other) h = other.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 } reject { |e| h[e] > 0 && h[e] -= 1 } end end 

此方法类似于Array# – 。 以下示例说明了不同之处:

 a = [3,1,2,3,4,3,2,2,4] b = [2,3,4,4,3,4] a - b #=> [1] c = a.difference b #=> [1, 3, 2, 2] 

如你所见, a包含三个3, b包含两个,因此在构造ca未变异)中删除a中的两个3。 当b包含的元素实例数量少于ac包含该元素的实例。 要删除从以下结尾开始的元素:

 a.reverse.difference(b).reverse #=> [3, 1, 2, 2] 

Array#difference! 可以用明显的方式定义。

我发现这种方法有很多用途: 这里 , 这里 , 这里 , 这里 , 这里 , 这里 , 这里 , 这里 , 这里 , 这里 , 这里 , 这里 , 这里 , 这里 , 这里 , 这里 , 这里 , 这里 , 这里 , 这里 , 这里 , 这里和这里 。

我已经建议将此方法添加到Ruby核心中。

Array#- ,此方法可以轻松地从数组中提取唯一元素a

 a = [1,3,2,4,3,4] u = a.uniq #=> [1, 2, 3, 4] u - a.difference(u) #=> [1, 2] 

这是因为

 a.difference(u) #=> [3,4] 

包含a的所有非唯一元素(每个可能不止一次)。

手头的问题

 class Array def uniq_elements(&prc) prc ||= ->(e) { e } a = map { |e| prc[e] } u = a.uniq uniques = u - a.difference(u) select { |e| uniques.include?(prc[e]) ? (uniques.delete(e); true) : false } end end 

例子

 t = [1,2,2,3,4,4,5,6,7,7,8,9,9,9] t.uniq_elements #=> [1,3,5,6,8] t = [1.0, 1.1, 2.0, 3.0, 3.4, 4.0, 4.2, 5.1, 5.7, 6.1, 6.2] t.uniq_elements { |z| z.round } # => [2.0, 5.1] 

这是另一种方式。

 require 'set' class Array def uniq_elements(&prc) prc ||= ->(e) { e } uniques, dups = {}, Set.new each do |e| k = prc[e] ((uniques.key?(k)) ? (dups << k; uniques.delete(k)) : uniques[k] = e) unless dups.include?(k) end uniques.values end end 

例子

 t = [1,2,2,3,4,4,5,6,7,7,8,9,9,9] t.uniq_elements #=> [1,3,5,6,8] t = [1.0, 1.1, 2.0, 3.0, 3.4, 4.0, 4.2, 5.1, 5.7, 6.1, 6.2] t.uniq_elements { |z| z.round } # => [2.0, 5.1] 

说明

  • 如果使用块调用uniq_elements ,则将其作为proc prc接收。
  • 如果在没有块的情况下调用uniq_elements ,则prcnil ,因此该方法的第一个语句将prc设置为等于默认的proc(lambda)。
  • 最初为空的散列, uniques ,包含唯一值的表示。 值是数组self的唯一值,键是proc proc传递数组值时返回的值,并且调用: k = prc[e]
  • set dups包含已发现不唯一的数组元素。 它是一个加速查找的集合(而不是数组)。 或者,如果可以是具有非唯一值作为键和任意值的散列。
  • 对数组self每个元素e执行以下步骤:
    • k = prc[e]
    • 如果dups包含ke就是dup,所以不需要再做了; 其他
    • 如果uniques具有密钥k ,则e是dup,因此k被添加到集合dup中,并且具有密钥k的元素从dups中移除; 其他
    • 元素k=>e被添加到独特元素作为唯一元素的候选者。
  • 返回unique值。
 class Array def uniq_elements counts = Hash.new(0) arr = map do |orig_val| converted_val = block_given? ? (yield orig_val) : orig_val counts[converted_val] += 1 [converted_val, orig_val] end uniques = [] arr.each do |(converted_val, orig_val)| uniques << orig_val if counts[converted_val] == 1 end uniques end end t=[1,2,2,3,4,4,5,6,7,7,8,9,9,9] p t.uniq_elements t=[1.0, 1.1, 2.0, 3.0, 3.4, 4.0, 4.2, 5.1, 5.7, 6.1, 6.2] p t.uniq_elements { |elmt| elmt.round } --output:-- [1, 3, 5, 6, 8] [2.0, 5.1] 

Array #uniq没有找到非重复元素,而是Array #uniq删除重复项。

 class Array def uniq_elements zip( block_given? ? map { |e| yield e } : self ) .each_with_object Hash.new do |(e, v), h| h[v] = h[v].nil? ? [e] : false end .values.reject( &:! ).map &:first end end [1,2,2,3,4,4,5,6,7,7,8,9,9,9].uniq_elements #=> [1, 3, 5, 6, 8] [1.0, 1.1, 2.0, 3.0, 3.4, 4.0, 4.2, 5.1, 5.7, 6.1, 6.2].uniq_elements &:round #=> [2.0, 5.1] 
  1. 创建和调用默认proc是浪费时间,并且
  2. 使用折磨的结构将所有内容填充到一行不会使代码更有效 – 它只会使代码更难理解。
  3. 在require语句中,rubyists不会将文件名大写。

….

 require 'set' class Array def uniq_elements uniques = {} dups = Set.new each do |orig_val| converted_val = block_given? ? (yield orig_val) : orig_val next if dups.include? converted_val if uniques.include?(converted_val) uniques.delete(converted_val) dups << converted_val else uniques[converted_val] = orig_val end end uniques.values end end t=[1,2,2,3,4,4,5,6,7,7,8,9,9,9] p t.uniq_elements t=[1.0, 1.1, 2.0, 3.0, 3.4, 4.0, 4.2, 5.1, 5.7, 6.1, 6.2] p t.uniq_elements {|elmt| elmt.round } --output:-- [1, 3, 5, 6, 8] [2.0, 5.1]