如何选择独特的元素

时间:2014-07-28 00:32:00

标签: ruby arrays uniq

我想使用Array方法扩展uniq_elements类,该方法返回多重性为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]

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

6 个答案:

答案 0 :(得分:11)

帮助方法

此方法使用帮助程序:

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&{39},b包含两个,因此首先 {3}中的两个3&#39}在构建a时删除(c未变异)。当a包含与b一样多的元素实例时,a不包含该元素的实例。要删除从c结尾处开始的元素:

a

a.reverse.difference(b).reverse #=> [3, 1, 2, 2] 可以用明显的方式定义。

我发现此方法有很多用途:herehereherehereherehere,{{ 3}},hereherehereherehereherehere,{{3} },herehereherehereherehereherehere

我有here将此方法添加到Ruby核心。

Array#difference!一起使用时,此方法可以轻松从数组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

答案 1 :(得分:3)

这是另一种方式。

<强>代码

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_elementsprcnil,则方法的第一个语句将prc设置为等于默认的proc(lambda)。< / LI>
  • 最初为空的哈希uniques,包含唯一值的表示。值是数组self的唯一值,键是proc prc传递数组值并调用时返回的值:k = prc[e]
  • 集合dups包含已发现不唯一的数组元素。它是一个加速查找的集合(而不是数组)。或者,如果可以是具有非唯一值作为键和任意值的散列。
  • 对数组e的每个元素self执行以下步骤:
      计算
    • k = prc[e]
    • 如果dups包含ke是一个副本,那么就不需要做任何事了;其他
    • 如果uniques有一个密钥ke是一个副本,那么k会被添加到集合dups和带有密钥{{}的元素1}}已从k中移除;其他
    • 元素uniques被添加到k=>e作为唯一元素的候选者。
  • 返回uniques的值。

答案 2 :(得分:1)

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删除了重复的元素。

答案 3 :(得分:0)

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]

答案 4 :(得分:0)

  1. 创建和调用默认proc是浪费时间,
  2. 使用折磨的构造将所有内容塞进一行并不会使代码更有效 - 它只会让代码更难理解。
  3. 在require语句中,rubyists不会将文件名大写。
  4. ...

    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]
    

答案 5 :(得分:0)

使用Enumerable#tally

class Array
  def uniq_elements
    tally.select { |_obj, nb| nb == 1 }.keys
  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]

如果您使用的是Ruby <2.7,则可以使用backports gem来获得tally

require 'backports/2.7.0/enumerable/tally'