Return variables and their matches in ets select

I was wondering if there is a way to return both the variables (e.g., :“$1”), and its matched value with a single call to :ets.select(table, match_spec).

Assuming, for example, that we have an ets table called :foo, which is filled with entries of, for example, {:a, %{b: :c}}, and a match specification like [{{:a, %{b: :"$1"}, [], [something_here]}]. Is there a way to define some functions in the match specification’s body such that I get a map of “variable-atom” to “matched-value”, or similar?

So, for example:

iex(1)> :ets.new(:foo, [:bag, :public, :named_table])
iex(2)> :ets.insert(:foo, {:a, %{b: :c}})
iex(3)> :ets.select(:foo, [{{:a, %{b: :"$1"}}, [], [:"$1"]}])

returns

[:c]

but what I’m hoping to find is a way to have :ets.select return for this example

%{"$1": :c}

or anything like that.

I know that there is the match body variable :"$$" that returns a list of all the variables’ values. The issue is just that the match_spec is dynamically created, and I don’t know which variables (i.e., which integer numbers after the dollar sign) are in the match spec at the time of the select call. I need those to construct guards for a second select call.

Of course, this is a very small example. In my case, the terms stored in the ets table are much more complex, making it very hard to iterate over them to check for variable-value matches.

Any help would be appreciated.

The last element in MatchSpec might be whatever, so you might go with something like

iex|💧|1 ▸ :ets.select(:foo, [{{:a, %{b: :"$1"}}, [], [{{:foo, :"$1"}}]}])
[foo: :c]

if I understood what you are after properly.

Thanks for the reply, but I’m in particular searching for a solution to get the variable (e.g., :"$1" out of the select call. But I don’t know which variable it is in advance, so I don’t know how I could specify this in the body. I was hoping for something like [:"$$"], but instead of getting a list with the bound values, I’d get a list of the bound values and the variable that it was bound to.

Atoms of the form :"$N" are pretty much the only literals you cannot return out of a match spec verbatim, so I suggest you simply come up with your own tagging convention and inject them into your matchspecs when selecting values; for example, raw integers.

It’s hard to envision quite what problem you are describing without example code of how you are composing your MSs dynamically. Maybe you need to pass an integer accumulator throughout your piece-wise composition of each spec clause, and increment it when you generate a new binding, so you know what values to take out and tag afterwards?