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!

Last Post!

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

lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
greenz1
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
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
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New

Other popular topics Top

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
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
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 31586 112
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New

We're in Beta

About us Mission Statement