tl;dr frequencies ftw

A function I frequently find useful takes a list of values, whatever they may be, and returns a map of those values to the number of times they appear in the list. Concretely, it takes a list like this:

["a", "b", "c", "b", "a", "d", "a"]

And returns a dictionary like this:

{ "a": 3, "b": 2, "c": 1, "d": 1 }

Let’s call such a hypothetical function frequencies, and take a look at how we might implement it in a few languages.


def frequencies(xs)
  freqs = { |hash, key| hash[key] = 0 }
  xs.each_with_object(freqs) do |x, freqs|
    freqs[x] += 1

strs = %w[a b c b a d a]
p frequencies(strs)

Not bad! I love Ruby’s facility for providing a block to set bthe default value for a hash entry.


I guess Ruby’s authors realized how useful frequencies is, because it’s been added in Ruby 2.7! (In release candidates at the time of writing.) In Ruby, it’s called tally. ❤️

%w[a b c b a d a].tally
# => {"a"=>3, "b"=>2, "c"=>1, "d"=>1}


package main

import "fmt"

func frequencies(things []interface{}) map[interface{}]int {
	freqs := make(map[interface{}]int)
	for _, thing := range things {
		freqs[thing] += 1
	return freqs

func main() {
	strs := []string{"a", "b", "c", "b", "a", "d", "a"}
	var whatevs []interface{}
	for _, str := range strs {
		whatevs = append(whatevs, str)
	fmt.Printf("%v\n", frequencies(whatevs))

Not as terse as Ruby, but I wouldn’t expect that from Go. A thing I like is leaning on the default value of the int type; I don’t need to initialize every value to 0 before incrementing the count.


use std::collections::HashMap;
use std::hash::Hash;

fn frequencies<T: Copy + Eq + Hash>(xs: &Vec<T>) -> HashMap<T, i32> {
    let mut freqs = HashMap::new();
    for x in xs {
        let count = freqs.get(x).unwrap_or(&0) + 1;
        freqs.insert(*x, count);

fn main() {
    let strs = vec!["a", "b", "c", "b", "a", "d", "a"];
    println!("{:?}", frequencies(&strs));

A little shorter than Go, and of course it handles generics a little more robustly than Go. However, this version will only work on a homogeneous collection. (All the other examples here, Go’s included, will work with whatever you give ’em.) There are probably ways to handle that in Rust, but my burgeoning Rust-fu is not up to the task.


(println (frequencies ["a" "b" "c" "b" "a" "d" "a"]))

Oh, look at that: frequencies is built into Clojure. Was this entire blog post elaborate theater, motivated by building to this cherry-picked, trollish point? You be the judge.

Tools Used