How to use Elixir pattern matching to check if a list's item startswith a given string(in a variable)?

55 Views Asked by At

I am new in Elixir and I would like to collect all the items of a list that startswith a given string. If I hardcode a the string as the left side of the <> operator, the program works otherwise I get error ArgumentError the left argument of <> operator inside a match should be always a literal binary as its size can't be verified.

defmodule StringStartsWith do
  def search([]), do: []
  def search(["Case"<> head | tail]), do: ["Case" <> head] ++ search(tail)
  def search([_head | tail]), do: search(tail)
end

StringStartsWith.search(["CaseDown", "YoHua", "CaseUp"])
|> IO.puts()

I want to know how can I use a variable as the left side for the <> operator which will hold the value of the starting sub string. A solution with a bit of explanation will really help my case.

Cheers, DD

1

There are 1 best solutions below

0
Adam Millerchip On BEST ANSWER

As the error message says, a variable can't be used in this way, because it has no way to know how long the string on the left should be. Consider this example:

foo <> bar = "foobar"

How does the language know how much of the string to assign to foo? However, this works:

"foo" <> bar = "foobar"

Because it's clear that "foo" is a three byte string that matches the beggining of "foobar".

This also works, using the pin operator:

foo = "foo"
^foo <> bar = "foobar"

This works, because rather than asking the language to guess what to assign to foo, we are telling it to re-use the existing value. Unfortunately we can't pin a variable from another function parameter:

def search([^foo <> bar | rest], foo) # can't do this

But you could still use patten matching inside the function, for example using a comprehension:

def search(list, string) do
  for ^string <> _ = word <- list, do: word
end

Or by using the Enum.filter/2 with the match?/2 function:

def search(list, string) do
  Enum.filter(list, &match?(^string <> _, &1))
end

However in this case I personally would use String.starts_with?/2:

def search(list, string) do
  Enum.filter(list, &String.starts_with?(&1, string))
end