First of all, if performance is not important to you, you can pass all the work into a regular expression:
~r/\A(.{0,30})(?:-|\Z)/
I assume this will be the shortest solution, but not effective:
iex(28)> string "another-long-string-to-be-truncated-and-much-text-here" iex(29)> string2 "another-long-string-to-be-cool-about-that" iex(30)> Regex.run(~r/\A(.{0,30})(?:-|\Z)/, string) |> List.last() "another-long-string-to-be" iex(31)> Regex.run(~r/\A(.{0,30})(?:-|\Z)/, string2) |> List.last() "another-long-string-to-be-cool"
Effective solution
But if you care about performance and memory, then I suggest the following:
defmodule CoolSlugHelper do def slug(input, length \\ 30) do length_minus_1 = length - 1 case input do # if the substring ends with "-" # ie "abc-def-ghi", 8 or "abc-def-", 8 -> "abc-def" <<result::binary-size(length_minus_1), "-", _::binary>> -> result # if the next char after the substring is "-" # ie "abc-def-ghi", 7 or "abc-def-", 7 -> "abc-def" <<result::binary-size(length), "-", _::binary>> -> result # if it is the exact string. ie "abc-def", 7 -> "abc-def" <<_::binary-size(length)>> -> input # return an empty string if we reached the beginnig of the string _ when length <= 1 -> "" # otherwise look into shorter substring _ -> slug(input, length_minus_1) end end end
It does not collect the resulting char-by-char string. Instead, it searches for the correct substring, starting from the required length to 1. Thus, it becomes efficient in terms of memory and speed.
We need this length_minus_1 variable because we cannot use the expression in binary-size mapping of binary numbers.
Here is the standard of all proposed solutions as of December 22, 2018:
(The simple regular expression is ~r/\A(.{0,30})(?:-|\Z)/ regular expression above)
Name ips average deviation median 99th % CoolSlugHelper 352.14 K 2.84 μs ±1184.93% 2 μs 8 μs SlugHelper 70.98 K 14.09 μs ±170.20% 10 μs 87 μs Simple Regex 33.14 K 30.17 μs ±942.90% 21 μs 126 μs Truncation 11.56 K 86.51 μs ±84.81% 62 μs 299 μs Comparison: CoolSlugHelper 352.14 K SlugHelper 70.98 K - 4.96x slower Simple Regex 33.14 K - 10.63x slower Truncation 11.56 K - 30.46x slower Memory usage statistics: Name Memory usage CoolSlugHelper 2.30 KB SlugHelper 12.94 KB - 5.61x memory usage Simple Regex 20.16 KB - 8.75x memory usage Truncation 35.36 KB - 15.34x memory usage