Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Equivalent of Python's Counter in Ruby

Tags:

python

ruby

Python has the Counter class in the collections module. It is a class for counting hashable objects. E.g.:

cnt = Counter()
cnt['Anna'] += 3
cnt['John'] += 2
cnt['Anna'] += 4
print(cnt)
=> Counter({'Anna': 7, 'John': 2})
print(cnt['Mario'])
=> 0

What is the equivalent of Counter in Ruby?

EDIT:

The Counter class provides also the following mathematical operations and helper methods:

c = Counter(a=3, b=1)
d = Counter(a=1, b=2)
c + d 
=> Counter({'a': 4, 'b': 3})
c - d
=> Counter({'a': 2})
c.most_common(1)
=> ['a']
like image 823
Marco Avatar asked Nov 20 '15 13:11

Marco


People also ask

Is there a Counter in Python?

Counter is a subclass of dict that's specially designed for counting hashable objects in Python. It's a dictionary that stores objects as keys and counts as values. To count with Counter , you typically provide a sequence or iterable of hashable objects as an argument to the class's constructor.

What is the Counter method in Python?

A Counter is a dict subclass for counting hashable objects. It is a collection where elements are stored as dictionary keys and their counts are stored as dictionary values. Counts are allowed to be any integer value including zero or negative counts.

How do I get an item of a Counter in Python?

Accessing Elements in Python Counter To get the list of elements in the counter we can use the elements() method. It returns an iterator object for the values in the Counter.

Is Counter faster Python?

The question was about Python 2.7, but I mention that as of Python 3.2 Counter is faster, so as of 2012 (8 years ago).


2 Answers

cnt = Hash.new(0)
cnt["Anna"] += 3
cnt["John"] += 2
cnt["Anna"] += 4
cnt # => {"Anna" => 7, "John" => 2}
cnt["Mario"] #=> 0

c = {"a" => 3, "b" => 1}
d = {"a" => 1, "b" => 2}
c.merge(d){|_, c, d| c + d} # => {"a" => 4, "b" => 3}
c.merge(d){|_, c, d| c - d}.select{|_, v| v > 0} # => {"a" => 2}
c.max(1).map(&:first) # => ["a"]
like image 82
sawa Avatar answered Sep 30 '22 03:09

sawa


Here's a small implementation:

class Counter < Hash
  def initialize(other = nil)
    super(0)
    if other.is_a? Array 
      other.each { |e| self[e] += 1 }
    end
    if other.is_a? Hash
      other.each { |k,v| self[k] = v }
    end
    if other.is_a? String
      other.each_char { |e| self[e] += 1 }
    end
  end
  def +(rhs)
     raise TypeError, "cannot add #{rhs.class} to a Counter" if ! rhs.is_a? Counter  
     result = Counter.new(self)
     rhs.each { |k, v| result[k] += v }
     result
  end
  def -(rhs)
     raise TypeError, "cannot subtract #{rhs.class} to a Counter" if ! rhs.is_a? Counter  
     result = Counter.new(self)
     rhs.each { |k, v| result[k] -= v }
     result
  end
  def most_common(n = nil)
     s = sort_by {|k, v| -v}
     return n ? s.take(n) : s
  end
  def to_s
     "Counter(#{super.to_s})"
  end
  def inspect
     to_s
  end
end

It supports

  • construction from strings, arrays and hashes
  • accessing and modifying counts
  • addition and substraction for two Counters
  • access for the most_common element(s)

c1 = Counter.new([1,0,1])   #=> Counter({1=>2, 0=>1})
c1[2] = 1                   #=> 1 
c1                          #=> Counter({1=>2, 0=>1, 2=>1})
c2 = Counter.new([3,1])     #=> Counter({3=>1, 1=>1})
c1 + c2                     #=> {1=>3, 0=>1, 2=>1, 3=>1}
c3 = Counter.new("abraca")  #=> {"a"=>3, "b"=>1, "r"=>1, "c"=>1}
c3.most_common(2)           #=> [["a", 3], ["b", 1]]
like image 25
Karoly Horvath Avatar answered Sep 30 '22 03:09

Karoly Horvath