【问题标题】:Counting matching elements in an array计算数组中的匹配元素
【发布时间】:2015-06-14 16:43:34
【问题描述】:

给定两个大小相等的数组,我怎样才能找到不考虑位置的匹配元素的数量?
例如:

  1. [0,0,5][0,5,5] 将返回 2 的匹配项,因为共有一个 0 和一个 5
  2. [1,0,0,3][0,0,1,4] 将返回匹配 3,因为有两个匹配 0 和一个匹配 1;
  3. [1,2,2,3][1,2,3,4] 将返回匹配 3

我尝试了许多想法,但它们都倾向于变得相当粗糙和令人费解。我猜有一些不错的 Ruby 习语,或者可能是一个正则表达式,这将是这个解决方案的一个优雅的答案。

【问题讨论】:

  • 我的第一个猜测是array_one & array_two.length,但这不包括独特的元素
  • 好问题,说得好。
  • @CarySwoveland:完全同意你的观点,但标题含糊不清——有同样问题的人不太可能看到这篇文章。附言我也很难为此制定一个不繁琐的标题..
  • @suslov,你说得有道理。怎么样,“确定一个数组中将 1-1 映射到第二个数组中相同元素的元素的数量。”?
  • @CarySwoveland,喜欢这张照片!这太棒了。对于这个措辞不佳的问题,我很抱歉,但我有一段时间用文字表达了一些例子中更容易展示的内容。感谢您的所有帮助。

标签: arrays ruby


【解决方案1】:

你可以通过count来完成它:

a.count{|e| index = b.index(e) and b.delete_at index }

Demonstration

inject:

a.inject(0){|count, e| count + ((index = b.index(e) and b.delete_at index) ? 1 : 0)}

Demonstration

或者selectlength或者它的别名size):

a.select{|e| (index = b.index(e) and b.delete_at index)}.size

Demonstration

结果:

  1. a, b = [0,0,5], [0,5,5] 输出: => 2;
  2. a, b = [1,2,2,3], [1,2,3,4] 输出: => 3;
  3. a, b = [1,0,0,3], [0,0,1,4] 输出 => 3.

【讨论】:

  • 我认为这就是解决方案!我需要在上面运行我的 rspec,因为我在自己的尝试中遇到了一些边缘情况,但我一直在祈祷。我的套件运行后会通知您。同时,非常感谢您。
  • 它通过了我的测试!再次感谢你,suslov,我已经在我的源代码中感谢了你。我的义务。
  • 不错。您可以使用a.select{|e| ndx = b.index(e) && b.delete_at ndx }.count 减少工作量。请注意,如果存在 nil 元素,这将不起作用。此外,它会变异 b,因此您可能希望对 b 的副本进行操作。
  • 是的。 (我认为后者更符合inject 的工作方式。)
  • 我只需要说select 是一个真的很好的 ruby​​ 代码!我在这里提问的最初动机是学习“Ruby 方式”,而且我受过良好的教育。简直太美了!事实上,我很惊讶。再次感谢,我的代码运行良好,我很乐意添加新功能,而不是敲我的脑袋。
【解决方案2】:
(arr1 & arr2).map { |i| [arr1.count(i), arr2.count(i)].min }.inject(0, &:+)

这里(arr1 & arr2)返回两个数组包含的uniq值列表,arr.count(i)计算数组中i的项目数。

【讨论】:

  • 那行不通,因为你可以拥有多个相同的值
  • 啊,对不起。我误解了这个问题。一会儿。我会更新的
  • 已更新。有趣的是,我以前不知道count(i) :)
  • 太棒了!迄今为止最好的答案,imo,但我建议稍微简化一下:arr1.uniq.reduce(0) { |t,e| t+[arr1.count(e), arr2.count(e)].min }.
【解决方案3】:

强大(并且非常需要)Array#difference 的另一种用途,我在我的回答 here 中定义了它。此方法类似于Array#-。下面的例子说明了这两种方法的区别:

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

对于目前的应用:

def number_matches(a,b)
  left_in_b = b
  a.reduce(0) do |t,e|
    if left_in_b.include?(e)
      left_in_b = left_in_b.difference [e]
      t+1
    else
      t
    end
  end
end

number_matches [0,0,5],   [0,5,5]   #=> 2
number_matches [1,0,0,3], [0,0,1,4] #=> 3
number_matches [1,0,0,3], [0,0,1,4] #=> 3

【讨论】:

    【解决方案4】:

    使用multiset gem:

    (Multiset.new(a) & Multiset.new(b)).size
    

    Multiset 类似于Set,但允许重复值。 & 是“集合交集”运算符(返回两个集合中的所有事物)。

    【讨论】:

    • 来自 ruby​​gems.org 的描述:与普通的集合不同(参见“set”库的 Ruby 文档),多重集合可以包含两个或多个相同的项目。 Set[:a,:b,:c,:b,:b,:c] # => # Multiset[:a,:b,:c,: b,:b,:c] # => # 多重集通常用于计算元素及其在集合中的出现次数。很有意思。感谢您让我了解这颗宝石。
    【解决方案5】:

    我认为这不是一个理想的答案,因为它有点复杂,但是...

    def count(arr)
      arr.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
    end
    
    def matches(a1, a2)
      m = 0
      a1_counts = count(a1)
      a2_counts = count(a2)
      a1_counts.each do |e, c|
        m += [a1_counts, a2_counts].min
      end
      m
    end
    

    基本上,首先编写一个方法,从每个元素出现次数的数组中创建一个散列。然后,使用这些来总结每个元素在两个数组中出现的最少次数。

    【讨论】:

    • 你忘了结束 ] in matches +=` 行
    • 谢谢,是的,我只是把这个放在一起,所以可能存在语法问题。
    • 我在 irb 中试过这个。现在我收到undefined method '+' for nil:Nilclass
    • 好了。在回答之前应该让我的代码工作,但我认为它现在工作了。
    • 您的方法count 有时被称为“计数哈希”,通常写成def count(arr); arr.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }; end
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-06-12
    • 2021-12-21
    • 1970-01-01
    • 1970-01-01
    • 2018-12-31
    • 2023-02-24
    • 2015-06-21
    相关资源
    最近更新 更多