【问题标题】:Find N number of common attributes in an array of structs在结构数组中查找 N 个公共属性
【发布时间】:2019-10-23 15:32:21
【问题描述】:

我有以下结构:

Name = Struct.new(:first_name, :last_name) do
  def greeting
    "Hello #{first_name}!"
  end
end

我将这些对象添加到这样的数组中:

full_names << Name.new(first_name, last_name)

现在,我想找出 N 个最常见的名字。

【问题讨论】:

  • 我觉得像full_names.uniq.map(&amp;:first_name) { |n| full_names.count(n) }.max 这样的东西是第一步,但这给了我一个块/参数错误。

标签: ruby


【解决方案1】:

您可以使用group_by 创建一个哈希映射每个名字到数组中所有出现的数组,transform_values 将字符串的每个值数组转换为计数,然后使用max_by 提取最大的n 计数。

Name = Struct.new(:first_name, :last_name) do
  def greeting
    "Hello #{first_name}!"
  end
end

full_names = [
  Name.new("a", "b"),
  Name.new("b", "c"),
  Name.new("d", "c"),
  Name.new("c", "d"),
  Name.new("d", "c"),
  Name.new("b", "b"),
  Name.new("b", "e")
]
n = 2

p full_names
  .group_by(&:first_name)
  .transform_values(&:size)
  .max_by(n, &:last)

输出:

[["b", 3], ["d", 2]]

如果您只想要名字而不想要计数,请将.map(&amp;:first) 附加到链中。

【讨论】:

  • 干得好ggorlen!我正在添加我在此期间提出的解决方案。
  • 我个人不喜欢块后面的[0...n]语法,宁愿在下面的行中使用.first(n).take(n),但这只是个人喜好。
  • 是的,这当然是有效的。毫无疑问,.first 在语义上更有意义。我来自 Python,切片几乎无处不在,所以看到 []s 对我来说往往比在我的大脑中解析一个英文单词更能立即点击,但我认为你的建议在 Ruby 中更惯用。
  • 顺便说一句,max_by(n, &amp;:last) 可以替换第一个示例中的.sort_by{|k, v| -v}.first(n)Enumerable#max_by 接受最大结果数的参数。
  • @engineersmnky 谢谢——每当我回答一个 Ruby 问题时,总会有人告诉我一些更好的函数!
【解决方案2】:
full_names = [
  Name.new("Bob", "Feller"),
  Name.new("Hank", "Jones"),
  Name.new("Annie", "Oakley"),
  Name.new("Cher", ""),
  Name.new("Annie", "Hall"),
  Name.new("Melba", "Toast"),
  Name.new("Bob", "Dylan"),
  Name.new("Hank", "Wiliams"),
  Name.new("Bob", "Marley")
]

nbr_most_common = 3

full_names.each_with_object(Hash.new(0)) { |i,h| h[i[:first_name]] += 1 }.
           max_by(nbr_most_common, &:last).
           map(&:first)
  #=> ["Bob", "Hank", "Annie"]

如果您还想显示频率,请将最后一行更改为to_h

full_names.each_with_object(Hash.new(0)) { |i,h| h[i[:first_name]] += 1 }.
           max_by(nbr_most_common, &:last).
           to_h
   #=> {"Bob"=>3, "Hank"=>2, "Annie"=>2} 

查看创建默认值(此处为零)和Enumerable#max_byHash::new 版本。

【讨论】:

    【解决方案3】:

    你可以在 2.7 中使用Enumerable#tally

    Name = Struct.new(:first_name, :last_name) do
      def greeting
        "Hello #{first_name}!"
      end
    end
    
    full_names = [
      Name.new("Yui", "Yoko"),
      Name.new("Bob", "Feller"),
      Name.new("Hank", "Jones"),
      Name.new("Annie", "Oakley"),
      Name.new("Cher", ""),
      Name.new("Annie", "Hall"),
      Name.new("Melba", "Toast"),
      Name.new("Bob", "Dylan"),
      Name.new("Hank", "Wiliams"),
      Name.new("Bob", "Marley")
    ]
    
    full_names.map(&:first_name).tally.max_by(3, &:last) 
    #=> [["Bob", 3], ["Annie", 2], ["Hank", 2]]
    

    【讨论】:

      【解决方案4】:

      使用相同的例子:

      Name = Struct.new(:first_name, :last_name) do
        def greeting
          "Hello #{first_name}!"
        end
      end
      
      full_names = [
        Name.new("a", "b"),
        Name.new("b", "c"),
        Name.new("d", "c"),
        Name.new("c", "d"),
        Name.new("d", "c"),
        Name.new("b", "b"),
        Name.new("b", "e")
      ]
      

      我们可以使用injectfirst_name =&gt; frequency创建一个Hash:

      first_names = full_names.map(&:first_name)
      first_names #["a", "b", "d", "c", "d", "b", "b"]
      hash_of_frequency = first_names.inject(Hash.new(0)) { |h,v| h[v] += 1; h }
      hash_of_frequency # [["b", 3], ["d", 2], ["a", 1], ["c", 1]]
      

      现在我们只需执行sort_by 并获得您的结果:

      hash_of_frequency.sort_by{|k, v| -v}.first(number_desired)
      

      结果是一样的,但我更喜欢使用inject

      【讨论】:

        【解决方案5】:

        这是我根据 Sophie 的回答 HERE 提出的解决方案。

        首先,这给了我一个名字的哈希值和每个名字的出现次数:

        freq_of_first_names = full_names.map(&:first_name).inject(Hash.new(0)) { |h,v| h[v] += 1; h }
         => "Haley"=>122, "Auer"=>119, "Lakin"=>96, "Macejkovic"=>98...
        

        现在我可以使用 .sort_by 方法对它进行排序,还可以添加 .last 方法以及我想要返回的记录数(在本例中为 10)。

        freq_of_first_names.sort_by {|_key, value| value}.last(10)
        

        【讨论】:

        • 顺便说一句max_by(10, &amp;:last) 可以替换.sort_by {|_key, value| value}.last(10)。 Enumerable#max_by 接受最大结果数的参数。
        猜你喜欢
        • 2017-09-05
        • 1970-01-01
        • 1970-01-01
        • 2015-12-15
        • 2011-07-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-07
        相关资源
        最近更新 更多