mercurio

mercurio

Capturing regex matching variables in cond

I have this function for parsing a string into a tuple. "-<number>" should return {:lat, -<number>}, "+<number>" should return {:lat, <number>}, and "<number>" should return {:gridNum, }`, plus some other simpler matches:

defp parseIndex(s) do
      cond do
        String.match?(s, ~r/^-\d+$/) ->
          [_, n] = Regex.run(~r/^-(\d+)$/, s)
          {:lat, -1 * String.to_integer(n)}
        String.match?(s, ~r/^\+\d+$/) ->
          [_, n] = Regex.run(~r/^\+(\d+)$/, s)
          {:lat, String.to_integer(n)}
        s == "--" ->
          {:latFrame, -1}
        s == "++" ->
          {:latFrame, 1}
        s == "-*" ->
          {:latEnd, -1}
        s == "+*" ->
          {:latEnd, 1}
        match?({_n, ""}, Integer.parse(s)) ->
          {:gridNum, String.to_integer(s)}
        true ->
          {:gridText, s}
      end
    end

This works, but is there a better way to do this without running the regex twice, once to match a pattern like -<number> and then again to extract the numerical portion? Same for the second-to-last clause, which ends up parsing the integer twice.

Thanks!

Phil

Most Liked

kip

kip

ex_cldr Core Team

Noting that:

  • Regex.run/2 returns nil if there is no match and that
  • nil is falsy for the purposes of boolean evaluation and
  • you can bind a variable in a match

For an optimisation I would likely put the explicit equality checks first since a cond proceeds in lexical order. Also I think you can collapse the :lat parsing into a single clause. And one last one, you can match and bind on the integer parsing too (the second last clause):

defp parseIndex(s) do
  cond do
    s == "--" ->
      {:latFrame, -1}
    s == "++" ->
      {:latFrame, 1}
    s == "-*" ->
      {:latEnd, -1}
    s == "+*" ->
      {:latEnd, 1}
    (match = Regex.run(~r/^([+-]\d+)$/, s)) -> 
      [_, n] = match
      {:lat, String.to_integer(n)}
    match?({n, ""}, Integer.parse(s)) ->
      {:gridNum, n}
    true ->
      {:gridText, s}
  end
end
edisonywh

edisonywh

What about doing binary matching directly?

defmodule Hello do
  def run(string) do
    do_run(string)
  end
  
  defp do_run("--"), do: {:latFrame, -1}
  defp do_run("++"), do: {:latFrame, 1}
  defp do_run("-*"), do: {:latEnd, -1}
  defp do_run("+*"), do: {:latEnd, 1}

  defp do_run(<<"+", number::binary>>), do: {:lon, to_integer(number)}
  defp do_run(<<"-", number::binary>>), do: {:lat, -to_integer(number)}
  defp do_run(<<"+", number::binary>>), do: {:lon, to_integer(number)}
  defp do_run(number), do: {:gridNum, to_integer(number)}
  
  defp to_integer(string), do: String.to_integer(string)
end
mercurio

mercurio

I like the solution with multiple functions, with a single Regex in the last one after handling all the simple cases. This is the best solution so far, thanks!

hauleth

hauleth

Here you have it with error handling

defmodule Hello do
  def run(string) do
    {:ok, do_run(string)}
  catch
    :throw, error -> {:error, error}
  end
  
  defp do_run("--"), do: {:latFrame, -1}
  defp do_run("++"), do: {:latFrame, 1}
  defp do_run("-*"), do: {:latEnd, -1}
  defp do_run("+*"), do: {:latEnd, 1}

  defp do_run(<<"+", number::binary>>), do: {:lon, to_integer(number)}
  defp do_run(<<"-", number::binary>>), do: {:lat, -to_integer(number)}
  defp do_run(<<"+", number::binary>>), do: {:lon, to_integer(number)}
  defp do_run(number), do: {:gridNum, to_integer(number)}
  
  defp to_integer(string) do
    case Integer.parse(string) do
      {num, ""} -> num
      _ -> throw({:not_number, string})
    end
  end
end

Where Next?

Popular in Questions Top

jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
sabri
Can someone explain the settings of pool_size of Ecto in config file? and what is the recommend size? Thanks
New
idi527
I’ve been re-reading swift book again and noticed that multiline strings there don’t have a trailing line break, unlike in elixir iex(2)...
New

Other popular topics Top

vac
Hi, I'm quite new in Elixir and I'm trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and ...
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 42633 214
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
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
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New

We're in Beta

About us Mission Statement