How to group numbers into different buckets in Ruby

后端 未结 3 1739
轮回少年
轮回少年 2021-01-01 02:51

I have a file with numbers on each line:

0101
1010
1311
0101
1311
431
1010
431
420

I want have a hash with the number of occurrences of eac

相关标签:
3条回答
  • 2021-01-01 03:25
    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 }
    

    You simply group the numbers by themselves using Enumerable#group_by, which gives you something like

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

    And then you Enumerable#map the value arrays to their lengths, i.e. [101, 101] becomes 2. Then just convert it back to a Hash using Hash::[].

    However, if you are willing to use a third-party library, it becomes even more trivial, because if you use a MultiSet data structure, the answer falls out naturally. (A MultiSet is like a Set, except that an item can be added multiple times and the MultiSet will keep count of how often an item was added – which is exactly what you want.)

    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>
    

    Yes, that's it.

    That's the beautiful thing about using the right data-structure: your algorithms become massively simpler. Or, in this particular case, the algorithm just vanishes.

    I've written more about using MultiSets for solving this exact problem at

    • find the repetition of duplicate numbers. (the first example in that post is the same as the second one here, the second example in that post is essentially the same as @Chuck's here and the third one is the same as the first in this post.)
    • Counting occurrences of unique letters in a string (the Ruby example is the same as here, the Scala and ECMAScript examples are the same as @Chuck's and the C# example is the same as my group_by example here.)
    • Bag/Multiset in scala and a touch of multimap (Again the same three examples as my two and @Chuck's in Scala.)
    0 讨论(0)
  • 2021-01-01 03:33

    Simple one-liner, given an array items:

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

    How it works:

    Hash.new(0) creates a new Hash where accessing undefined keys returns 0.

    inject(foo) iterates through an array with the given block. For the first iteration, it passes foo, and on further iterations, it passes the return value of the last iteration.

    Another way to write it would be:

    hash = Hash.new(0)
    items.each {|item| hash[item] += 1}
    
    0 讨论(0)
  • 2021-01-01 03:34

    This is essentially the same as Chuck's, but when you are creating an array or hash, 'each_with_object' will make it slightly simpler than 'inject', as you do not have to write the final array or hash in the block.

    items.each_with_object(Hash.new(0)) {|item, hash| hash[item] += 1}
    
    0 讨论(0)
提交回复
热议问题