Ruby 中保留重复元素的数组之间的区别

Difference Between Arrays Preserving Duplicate Elements in Ruby

我是 Ruby 的新手,希望能找出两个数组之间的区别。

我知道常用的方法:

a = [...]
b = [...]
difference = (a-b)+(b-a)

但问题在于这是在计算集合差,因为在ruby中,语句(a-b)定义了a相对于b的集合补码。

这意味着 [1,2,2,3,4,5,5,5,5] - [5] = [1,2,2,3,4],因为它会剔除第一组中出现的所有 5,而不仅仅是一个,其行为类似于对数据的筛选。

我希望它只删除一次差异,因此例如 [1,2,2,3,4,5,5,5,5][5] 的差异应该是 [1,2,2,3,4,5,5,5],只删除一个 5。

我可以迭代地执行此操作:

a = [...]
b = [...]

complimentAbyB = a.dup
complimentBbyA = b.dup

b.each do |bValue|
  complimentAbyB.delete_at(complimentAbyB.index(bValue) || complimentAbyB.length)
end
a.each do |aValue|
  complimentBbyA.delete_at(complimentBbyA.index(aValue) || complimentBbyA.length)
end

difference = complimentAbyB + complimentBbyA

但这似乎非常冗长且效率低下。我不得不想象有比这更优雅的解决方案。所以我的问题基本上是,找到两个数组差异的最优雅方法是什么,如果一个数组比另一个数组出现更多单个元素,它们不会全部被删除?

.....

ha = a.group_by(&:itself).map{|k, v| [k, v.length]}.to_h
hb = b.group_by(&:itself).map{|k, v| [k, v.length]}.to_h
ha.merge(hb){|_, va, vb| (va - vb).abs}.inject([]){|a, (k, v)| a + [k] * v}

hahb是以原数组中的元素为键,出现次数为值的散列。下面的合并将它们放在一起并创建一个散列,其值是两个数组中出现次数的差值。 inject 将其转换为一个数组,该数组的每个元素都按散列中给定的数字重复。


另一种方式:

ha = a.group_by(&:itself)
hb = b.group_by(&:itself)
ha.merge(hb){|k, va, vb| [k] * (va.length - vb.length).abs}.values.flatten

我最近提议将这种方法 Ruby#difference 添加到 Ruby 的核心。对于你的例子,它会被写成:

a = [1,2,2,3,4,5,5,5,5]
b = [5]

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

我经常举的例子是:

a = [3,1,2,3,4,3,2,2,4]
b = [2,3,4,4,3,4]

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

我首先在我的回答中提出了这种方法here。在那里你会找到我建议使用该方法的其他 SO 问题的解释和链接。

如链接所示,方法可以这样写:

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