daviaws

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 · GitHub

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

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

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

ityonemo

(Self promotion) you might find this library to be helpful for you to go either way:

ityonemo

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.

Last Post!

ityonemo

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.

Where Next?

Popular in Questions Top

New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New
sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New

Other popular topics Top

nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 54006 488
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 31494 112
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New

We're in Beta

About us Mission Statement