Search code examples
elixir

Extracting and rearranging substrings


I have a string containing a date in the form DD.MM.YYYY and would like to convert it to the format YYYYMMDD. No error checking is necessary, since I can ensure that the input string has the correct format. How can this be done in an compact way? NOTE: I'm interested in a solution doing it purely based on string processing, and not in creating and formatting Date objects.

I came up with the straightforward solution

s ="14.04.2023"
sx = String.slice(s, 6, 4) <> String.slice(s, 3, 2) <> String.slice(s, 0, 2)

This works, but there is a lot of typing. Is there a more "Elixir-like" approach? For instance, in Ruby I would perhaps do a

sx = s.match(/(..).(..).(....)/).captures.reverse.join

which would be clearer IMO, because the regex shows which parts of the string are extracted, and the reverse.join tells us how these parts are put together again. I wonder how a similar approach would be done in Elixir, or how experienced Elixir programmer would approach this task.


Solution

  • As Everett pointed out, binary pattern matching is the best way to approach this. Compared to Regular expressions it's much more explicit, reliable, and efficient.

    def format(<<dd::binary-2, ".", mm::binary-2, ".", yyyy::binary-4>>) do
      yyyy <> mm <> dd
    end
    

    Using it:

    iex(1)> Example.format("14.04.2023")
    "20230414"
    

    It also gives you a FunctionClauseError if you pass an invalid value:

    iex(2)> Example.format("14.04.23")
    ** (FunctionClauseError) no function clause matching in Example.format/1
    
        The following arguments were given to Example.format/1:
    
            # 1
            "14.04.23"
    

    You could also simplify your answer with String.split, but I think the binary matching is better. Some people find it hard to understand, so may prefer this, though:

    iex(3)> "14.04.2023" |> String.split(".") |> Enum.reverse() |> Enum.join()
    "20230414"