【问题标题】:How to group numbers into different buckets in Ruby如何在 Ruby 中将数字分组到不同的桶中
【发布时间】:2010-11-29 01:12:58
【问题描述】:

我有一个文件,每行都有数字:

0101
1010
1311
0101
1311
431
1010
431
420

我想要一个包含每个数字出现次数的哈希,在这种情况下:

{0101 => 2, 1010 => 2, 1311 => 2, 431 => 2, 420 => 1}

我该怎么做?

【问题讨论】:

标签: ruby


【解决方案1】:

简单的单行,给定一个数组items

items.inject(Hash.new(0)) {|hash, item| hash[item] += 1; hash}

它是如何工作的:

Hash.new(0) 创建一个新的哈希,访问未定义的键返回 0。

inject(foo) 使用给定块遍历数组。对于第一次迭代,它传递foo,在进一步的迭代中,它传递最后一次迭代的返回值。

另一种写法是:

hash = Hash.new(0)
items.each {|item| hash[item] += 1}

【讨论】:

  • 我倾向于使用@Chuck 展示的第二种方式,因为这样我就不必解释它了,尽管我更喜欢使用inject()
  • each_with_object 现在应该是首选的、更干净的解决方案 - 很容易忘记;散列。
【解决方案2】:

这与 Chuck 的基本相同,但是当您创建一个数组或散列时,“each_with_object”将使其比“注入”稍微简单一些,因为您不必在块中写入最终的数组或散列。

items.each_with_object(Hash.new(0)) {|item, hash| hash[item] += 1}

【讨论】:

    【解决方案3】:
    ID = -> x { x } # Why is the identity function not in the core lib?
    
    f = <<-HERE
      0101
      1010
      1311
      0101
      1311
      431
      1010
      431
      420
    HERE
    
    Hash[f.lines.map(&:to_i).group_by(&ID).map {|n, ns| [n, ns.size] }]
    # { 101 => 2, 1010 => 2, 1311 => 2, 431 => 2, 420 => 1 }
    

    您只需使用 Enumerable#group_by 将数字自己分组,这样就可以得到类似的结果

    { 101 => [101, 101], 420 => [420] }
    

    然后你 Enumerable#map 值数组到它们的长度,即 [101, 101] 变为 2。然后只需使用Hash::[] 将其转换回Hash

    但是,如果你愿意使用第三方库,那就变得更琐碎了,因为如果你使用MultiSet数据结构,答案自然就落空了。 (MultiSetSet 类似,不同之处在于可以多次添加一个项目,而 MultiSet 将记录添加项目的频率——这正是您想要的。)

    require 'multiset' # Google for it, it's so old that it isn't available as a Gem
    
    Multiset[*f.lines.map(&:to_i)]
    # => #<Multiset:#2 101, #2 1010, #2 1311, #2 431, #1 420>
    

    是的,就是这样。

    这就是使用正确数据结构的美妙之处:您的算法变得非常简单。或者,在这种特殊情况下,算法只是消失

    我已经写了更多关于使用MultiSets 来解决这个确切问题的文章

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-01-21
    • 2020-10-07
    • 2020-04-11
    • 2014-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多