'Erlang: ETS Matching With Guards (Select/2)'

Not every time we want to pull data out of an erlang ETS table is it as straightfoward as the previous example. Sometimes we want to get all values that are greater than zero, rather than just constants. We’ll need to use the ets:select function, which has support for guards.

Here’s a basic setup.

Tmp = ets:new(tmp, []).
ets:insert(Tmp, {bob, 0}).
ets:insert(Tmp, {jim, 1}).
ets:insert(Tmp, {jon, 2}).

% Returns all results
ets:select(Tmp,[{{'$1','$2'},[],['$$']}]).

% Returns results where $2 > 0 (jim and jon)

ets:select(Tmp,[{	{'$1','$2'},	[ { '>', '$2', 0 } ],['$$']	}]).

What’s going on in the select? There’s a LOT of syntax for something that’s so simple, but once we break it up a bit it starts to be easier to look at.

First off, lets take a look at the original spec.

MatchSpec = [MatchFunction]
MatchFunction = {MatchHead, [Guard], [Result]}
MatchHead = "Pattern as in ets:match"
Guard = {"Guardtest name", ...}
Result = "Term construct"

Pretty important stuff in here. The basic building block of the MatchSpec is a list. Inside the list, is a “matchfunction”.

[MatchFunction]

Next, we break the MatchFunction up a little bit more. MatchHead = Pattern. We’ve seen this before.

MatchSpec = [ {Pattern, [Guard], [Result] } ]

Now, lets write our basic pattern. We want the rows where the 2nd item is > 0. So, since we can’t put that in the pattern yet, we’ll just give it a reference.

MatchSpec = [ { {$1, $2}, [Guard], [Result] } ]

OK, cool. That’s not so bad anymore! Next step - lets use our guard. There’s a funky syntax for guards in the matchspec, so here we go. You’re looking at a list, right? So it can have many items. Each item is a record. They can nest. We’ll ignore that for now. What we need to know here is that in a pattern matching guard, we put the operation first. It would normally read $2 > 0.

[ { '>', '$2', 0} ]

That’ll give us the pattern we want. What about the Result? Feel free to just use ‘$$’ in there. It’ll just kick back what you specified in your pattern. If you don’t believe me,

8> ets:select(Tmp,[{ {'_','$2'},[ { '>', '$2', 0 } ],['$$']}]).
[[1],[2]]

Put it all together!

ets:select(Tmp,[{ {'$1','$2'},        [ { '>', '$2', 0 } ],        ['$$']  }]).

Not so bad anymore.

Match Spec docs for select/2

For testing purposes, you can use this bad boy.

test_ms(Tuple, MatchSpec)

You can also use this handy function to transform functions to selects:

fun2ms(LiteralFun) -> MatchSpec
If you found this post helpful, please consider sharing to your network. I'm also available to help you be successful with your distributed systems! Please reach out if you're interested in working with me, and I'll be happy to schedule a free one-hour consultation.