String pattern matching on first and last character

I’m curious if there is a better way to remove the first and last character of a given string using pattern matching.

Basically, if an initial string is - "hello", my function should output - "ello".

My very quick implementation is:

  def remove_letters(<<_::binary-size(1)>> <> tail) do
    length = byte_size(tail)
    <<result::binary-size(length - 1), ext::binary>> = tail

    result
  end

It works, but I’m curious if there is a way I can use a smarter pattern-matching approach in the function head to avoid extra steps in the function body?!

Thank you so much in advance!

I think your code would return ell since it removes both first and last bytes? One small note - your code only works on single-byte characters (not UTF-8).

On more recent OTP versions you can also do:

def remove_first_and_last(string) do
  <<_first::binary-1,mid::binary-size(byte_size(string) - 2),_last::binary-1>> = string
  mid
end
5 Likes

yeah, you may need to consider something like "😀Hello"

iex()> String.slice("😀Hello",1..-1)    
"Hello"

[head, tail] = Regex.run(~r/^.(.+).$/, “hello”)

would return “hello” in head and “ell” in tail

@outlog @simonrobins
Yeah, I’m pretty aware of those approaches.
I was specifically curious about string pattern matching!
Thank you though! I appreciate it!

1 Like

@kip
Yes!!! Exactly what I was curious about!
I knew it was possible!
Adding it to my knowledge library :slight_smile:
Thank you so much! Really really appreciate it!

Maybe a stupid question, but can it be done in the function head? Probably not, as you would have stated it that way, if it was possible.

You need to first get the size of the string, which cannot be done with pattern matching alone.

1 Like

This is firmly in “can but should not be done” territory, because String.reverse is expensive:

def remove_letters(<<_::binary-size(1)>> <> tail), do: do_remove_letters(String.reverse(tail))

defp do_remove_letters(<<_::binary-size(1)>> <> liat), do: String.reverse(liat)
2 Likes

If you know the start of the string (super rare) you could match using this:

iex(1)> fun = fn                        
...(1)> "h" <> rest -> rest
...(1)> other -> other  
...(1)> end 
#Function<42.3316493/1 in :erl_eval.expr/6>
iex(2)> fun.("hello")
"ello"
iex(3)> fun.("won't match")
"won't match"