How to Count Specific Items in a Collection with Elixir

So, I was working on this exercise over at Exercism.io, when I stumbled up an interesting function.

To make a long story short, I needed a count of a specific character (codepoint) in a character list i.e. 'hello' contains two ls. However, this solution could apply to any collection which implements the Enumerable protocol in Elixir.

I have been reading Programming in Elixir. The author, Dave Thomas, emphasizes the use of recursion and pattern matching throughout Elixir. So, of course, I had implemented the solution using recursion and pattern matching. You can see that solution here.

You Can Count (Ha!) on Elixir

Of course, I missed the obvious. Elixir already had a very convenient function for doing exactly this: Enum.count/2.

I had anticipated that Elixir would have a function to do a straight count of all the items in a collection (which it does: Enum.count/1 doc). However, I had not considered one for looking for a specific kind of item.

Well, Enum.count/2 provides a convenient API for this exact problem. You simply pass the enumerable (in my case, the character list) and a function which takes one argument (the current item) and will return true for whatever items you want to count.

Here’s a convenient graphic to demonstrate:

Elixir Enum Count

Elixir Enum.count/2 Examples

Of course, the expression used to evaluate the current item does not need to be a simple equality operation. Here are some examples using a variety of different operators:

Count all the binaries (strings) which equal “price”:

Enum.count(["Total", "retail", "price", "$14.95"], &(&1 == "price"))
1

Pipe syntax:

["Total", "retail", "price", "$14.95"]
  |> ... # some other functions
  |> Enum.count(&(&1 == "price"))

Count all the binaries which are members of a list:

roles = ["User", "Customer", "Partner", "Admin"]
Enum.count(roles, &Enum.member?(["Admin", "Editor", "Developer"], &1))
1

Count all the binaries (strings) which match a regular expression:

Enum.count(["Total", "price", "$14.95"], &(&1 =~ ~r/\$\d+\.\d+/))
1

Count all the binaries (strings) in a list:

Enum.count([1, "list", 3], &is_binary(&1))
1

Count all the binaries (strings) in a multi-type list:

Enum.count([1, "list", 3], &is_binary(&1))
1

Count all the integers in a multi-type list:

Enum.count([1, "list", 3], &is_integer(&1))
2

Count all the integers greater than 3:

Enum.count([1,2,4,8,16,32], &(&1 > 3))
4

Read the documentation on Enum.count/2

This post is part of my series of Elixir Tutorials


Hey, I’m Adam. I’m guessing you just read this post from somewhere on the interwebs. Hope you enjoyed it. When I’m not writing these blog posts, I’m a freelance Elixir and Ruby developer and working on Calculate, a product which makes it easier for you to create software estimates for your projects. Feel free to leave a comment if you have any questions.

You can also follow me on the Twitters at: @DeLongShot

  • hugolnx

    To count char in binaries you can also use bitstring comprehension:
    (for << <> >, c == ?l, do: 1) |> length()

    It is more efficient than converting it to a charlist, though is “uglier” hehe

    ## CountBinaryBench
    benchmark name average time
    bitstring comprehension 4.05 µs/op
    converting to charlist 12.46 µs/op

    benchmark using: https://github.com/alco/benchfella

    • Good point. Yeah, maybe not the ‘prettiest’ solution, but more efficient. Thanks for sharing!