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 l
s. 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/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.
You can also follow me on the Twitters at: @DeLongShot
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!