Ruby 3.1 Enumerable#tally now accepts an optional hash to count occurrences
source link: https://blog.saeloun.com/2021/05/19/enumerable-tally-with-hash-argrument
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Ruby 3.1 Enumerable#tally now accepts an optional hash to count occurrences
May 19, 2021 , by Rohit Joshi 3 minute readEnumeration refers to traversing over objects.
In Ruby, we have Enumerable classes like Array
, Hash
and Range
which get their
enumeration features by including the Enumerable
module.
This Enumerable
module provides various methods
#include?
, #count
, #map
, #select
and #uniq
which we frequently use,
and of course #tally
about which this blog is.
#tally
counts the number of occurrences of an element in a collection and
returns a hash containing the count for all elements.
To know more about Enumerable#tally
that got introduced in Ruby 2.7, check out our previous
blog.
Example
Consider the example of a stationery store. The shopkeeper sells his/her goods on a per-unit basis.
This type of problem is a perfect candidate to leverage our #tally
method to
calculate the total quantity of items sold in a week.
Consider the customers add items one by one to place an order as follows:
order_one = %i{ pen pencil eraser sharpener pen pen }
order_two = %i{ sharpener eraser eraser eraser eraser sharpener sharpener sharpener sharpener }
Before
Calculating the quantity of each item
tally_one = order_one.tally
# => { pen: 3, pencil: 1, eraser: 1, sharpener: 1 }
tally_two = order_two.tally
# => { sharpener: 5, eraser: 4 }
weekly_tally = (order_one + order_two).tally # order_one, order_two, ..., order_seven
# => { pen: 3, pencil: 1, eraser: 5, sharpener: 6 }
As we can see in the above example, the quantity of erasers and sharpeners has increased.
We have concatenated order_one
and order_two
arrays and then called #tally
to get the combined
count of the items ordered.
After
To calculate the total at runtime, Ruby 3.1 Enumerable#tally now accepts an optional hash to count occurrences.
In this case, we can store the running tally of the number of items and pass it to the #tally
method.
order_one = %i{ pen pencil eraser sharpener pen pen }
order_two = %i{ sharpener eraser eraser eraser eraser sharpener sharpener sharpener sharpener }
weekly_tally = {}
weekly_tally = order_one.tally(weekly_tally)
# => {pen: 3, pencil: 1, eraser: 1, sharpener: 1}
weekly_tally = order_two.tally(weekly_tally)
# => {pen: 3, pencil: 1, eraser: 5, sharpener: 6}
# And so on
As we can see in the above example in Ruby 3.1, we have passed the weekly_tally
hash
as an argument to the #tally
method that stores the count of items.
The count of erasers and sharpeners in order_two
got added to the weekly_tally
hash,
returning the combined quantity for each item.
- A hash with a default value can be passed to the
#tally
method. The keys which are present in the array will ignore the default hash value and keys not present will return the default value instead ofnil
.
order = %i{ pen pencil eraser sharpener pen pen }
tally = order.tally(Hash.new 5)
# => { pen: 3, pencil: 1, eraser: 1, sharpener: 1 }
tally[:ruler]
# => 5
- Similarly,
Proc
can be passed to the#tally
method. If the key is present in the arrayProc
will be ignored. For keys not present in the array, theProc
gets executed.
order = %i{ pen pencil eraser sharpener pen pen }
tally = order.tally(Hash.new { puts "Proc called" })
# => { pen: 3, pencil: 1, eraser: 1, sharpener: 1 }
tally[:ruler]
Proc called
# => nil
- Keys that are not present in the array are merged with the resulting hash.
For eg.,
first_name
andlast_name
that are not present in theorder
array got merged as it is.
order = %i{ pen pencil eraser sharpener pen pen }
tally = order.tally({ first_name: "Sam", last_name: "Example" }) # Allowed
# => { first_name: "Sam", last_name: "Example", pen: 3, pencil: 1, eraser: 1, sharpener: 1 }
- Keys that are present in the array should only take integer value else it will raise a
TypeError
.
order = %i{ pen pencil eraser sharpener pen pen }
tally = order.tally({ pencil: "colored" }) # Not allowed
# => TypeError: wrong argument type String (expected Integer)
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK