daviaws
Need help with :ets.match - I always end up with an empty list []
I’m writing a module to test ets match.
It would to store client consume by month/year.
I had try to follow match spec… I even tried to do automagic translate of elixir functions with GitHub - ericmj/ex2ms: :ets.fun2ms for Elixir, translate functions to match specifications
nonetheless I always end up with an empty list []
Please… someone help me with list_period method…
defmodule ETS.Consume do
@moduledoc """
Consume tasks
r ETS.Consume
ETS.Consume.scenario()
table = ETS.Consume.start()
ETS.Consume.populate(table)
ETS.Consume.list(table, "12345678901")
ETS.Consume.list_period(table, "12345678901", 2, 2021, 10, 2021)
"""
import Ex2ms
@table :cons
@opts [:ordered_set, :protected]
def start do
:ets.new(@table, @opts)
end
def insert(ref, cpf_cnpj, mes, ano, consumo) do
key = {cpf_cnpj, mes, ano}
:ets.insert(ref, {key, consumo})
end
def list(table, cpf_cnpj) do
:ets.match(table, {{cpf_cnpj, :"$1", :"$2"}, :"$3"})
end
def list_period(table, cpf_cnpj, mes_inicio, ano_inicio, mes_fim, ano_fim) do
pattern = [{{{:"$1", :"$2", :"$3"}, :"$4"}, [{:<, :"$2", 2}], [true]}]
:ets.match(table, pattern)
end
def populate(table) do
insert(table, "12345678901", 1, 2021, 100)
insert(table, "12345678901", 2, 2021, 200)
insert(table, "12345678901", 3, 2021, 300)
insert(table, "12345678901", 4, 2021, 200)
insert(table, "12345678901", 5, 2021, 300)
insert(table, "12345678901", 6, 2021, 350)
insert(table, "12345678901", 7, 2021, 200)
insert(table, "12345678901", 8, 2021, 100)
insert(table, "12345678901", 9, 2021, 500)
insert(table, "12345678901", 10, 2021, 300)
insert(table, "12345678901", 11, 2021, 200)
insert(table, "12345678901", 12, 2021, 310)
insert(table, "12345678902", 1, 2021, 310)
end
def scenario do
table = ETS.Consume.start()
ETS.Consume.populate(table)
# ETS.Consume.list(table, "12345678901")
ETS.Consume.list_period(table, "12345678901", 2, 2021, 10, 2021)
end
def test() do
fun do {{ cpf_cnpj, mes, ano }, consumo} = arg when mes < 2 -> true end
end
end
Marked As Solved
daviaws
Complete solution thank to @al2o3cr answer
defmodule ETS.Consume do
@moduledoc """
History by key, month, age, value
r ETS.Consume
ETS.Consume.scenario()
table = ETS.Consume.start()
ETS.Consume.populate(table)
ETS.Consume.list(table, "12345678901")
ETS.Consume.list_period(table, "12345678901", 2, 2021, 10, 2021)
"""
@table :cons
@opts [:ordered_set, :protected]
def start do
:ets.new(@table, @opts)
end
def index(age, month) do
age * 100 + month
end
def insert(ref, key, month, age, bytes) do
index = index(age, month)
key = {key, index, age, month}
:ets.insert(ref, {key, bytes})
end
def list(table, key) do
:ets.match(table, {{key, :_, :"$1", :"$2"}, :"$3"})
end
def list_period(table, key, begin_month, begin_age, end_month, end_age) do
begindex = index(begin_age, begin_month)
endex = index(end_age, end_month)
# :ets.fun2ms(
# fn {{key, index, month, age}, bytes} = _entry when
# key == "12345678901" and
# index >= 202301 and index <= 202312 ->
# {month, age, bytes}
# end)
match_spec = [
{{{:"$1", :"$2", :"$3", :"$4"}, :"$5"},
[
{:andalso, {:andalso, {:==, :"$1", key}, {:>=, :"$2", begindex}},
{:"=<", :"$2", endex}}
], [{{:"$3", :"$4", :"$5"}}]}
]
:ets.select(table, match_spec)
end
def populate(table) do
insert(table, "12345678901", 1, 2021, 100)
insert(table, "12345678901", 2, 2021, 200)
insert(table, "12345678901", 3, 2021, 300)
insert(table, "12345678901", 4, 2021, 200)
insert(table, "12345678901", 5, 2021, 300)
insert(table, "12345678901", 6, 2021, 350)
insert(table, "12345678901", 7, 2021, 200)
insert(table, "12345678901", 8, 2021, 100)
insert(table, "12345678901", 9, 2021, 500)
insert(table, "12345678901", 10, 2021, 300)
insert(table, "12345678901", 11, 2021, 200)
insert(table, "12345678901", 12, 2021, 310)
insert(table, "12345678901", 1, 2022, 410)
insert(table, "12345678902", 1, 2021, 310)
end
def scenario do
table = ETS.Consume.start()
ETS.Consume.populate(table)
# ETS.Consume.list(table, "12345678901") |> dbg()
ETS.Consume.list_period(table, "12345678901", 1, 2021, 9, 2021)
end
end
Also Liked
al2o3cr
There’s an unfortunate overlap in terminology: :ets.match takes a “match pattern”, not a match specification. The documentation for :ets.select/2 demonstrates this:
ets:match(Table,{'$1','$2','$3'})
# is equivalent to
ets:select(Table,[{{'$1','$2','$3'},[],['$$']}])
Demo:
iex(1)> tab = :ets.new(:foo, [:ordered_set])
#Reference<0.3893230368.2635726851.91982>
iex(3)> :ets.insert(tab, {{"a", 1, "b"}, "c"})
true
iex(4)> :ets.insert(tab, {{"d", 2, "e"}, "f"})
true
iex(6)> :ets.select(tab, [{{{:"$1", :"$2", :"$3"}, :"$4"}, [{:<, :"$2", 2}], [:"$_"]}])
[{{"a", 1, "b"}, "c"}]
ityonemo
(Self promotion) you might find this library to be helpful for you to go either way:
ityonemo
for elixir, fun2ms only works in the shell.
Also, MatchSpec has “ms2fun”, so you can debug in the other direction if it’s not doing what you expect.







