Inspired by my little son's eagerness to count when he sees groups of things that (he thinks) can be grouped together these days, I wanted to post my often used, tiny, fairly naïve extension to Ruby's Array class that I call count_by.
This might not be the most beautiful way to solve this, but it is fairly readable and is leveraging some key aspects of the Ruby language - open classes, or monkey patching if you must, and dynamic calling of functions.
Basically I open up the class Array and add a count_by function to it - that takes as argument the name of the function one wants to count the elements by.
The function itself starts off by instantiating a Hash, called map, with default values of 0. For each element in the Array I then call the function by the name provided and increments the entry in map corresponding to the result.
Like this, if I have a array of, let's say Olympic medalist objects with functions nationality in list, I can easily say: list.count_by(:nationality) and get back number of medalists per nationality.
I guess a(n even) more Ruby way of implementing this would be as a mix of group_by and map, and another improvement could be to add count_by to the class Enumerable instead of Array. Come to think of it, to make this function more in line with group_by, I guess I should also rewrite it to take a block as an argument instead of just a function name.
OK. Something like this, then:
I wouldn't call it more readable, maybe, but arguably more elegant - and way more flexible. Now one can count the nationalities from the example above by: list.count_by {&:nationality}
This might not be the most beautiful way to solve this, but it is fairly readable and is leveraging some key aspects of the Ruby language - open classes, or monkey patching if you must, and dynamic calling of functions.
Basically I open up the class Array and add a count_by function to it - that takes as argument the name of the function one wants to count the elements by.
The function itself starts off by instantiating a Hash, called map, with default values of 0. For each element in the Array I then call the function by the name provided and increments the entry in map corresponding to the result.
Like this, if I have a array of, let's say Olympic medalist objects with functions nationality in list, I can easily say: list.count_by(:nationality) and get back number of medalists per nationality.
I guess a(n even) more Ruby way of implementing this would be as a mix of group_by and map, and another improvement could be to add count_by to the class Enumerable instead of Array. Come to think of it, to make this function more in line with group_by, I guess I should also rewrite it to take a block as an argument instead of just a function name.
OK. Something like this, then:
I wouldn't call it more readable, maybe, but arguably more elegant - and way more flexible. Now one can count the nationalities from the example above by: list.count_by {&:nationality}
Comments
Post a Comment