I’m currently learning Elixir (only been at it for about 2 weeks), and I’ve been working on a problem from Codewars where I need to:
1-Split a string into substrings separated by vowels (aeiou).
2-Calculate a score for each substring based on the sum of alphabet values (e.g., a=1, b=2, ..., z=26).
3- Find the maximum score among the substrings.
defmodule Kata do
def solve(s) do
alphabet_values =
for {char, index} <- Enum.with_index(?a..?z, 1), into: %{}, do: {<<char::utf8>>, index}
vowels = "aeiou"
{substrings, last_segment} =
s
|> String.graphemes()
|> Enum.reduce({[], ""}, fn char, {acc, current} ->
if !Enum.any?(String.split(vowels, ""), fn x -> x == char end) do
{acc, current <> char}
else
{[current | acc], ""}
end
end)
substrings = [last_segment | substrings]
substrings
|> Enum.map(fn substring ->
substring
|> String.graphemes()
|> Enum.map(&alphabet_values[&1])
|> Enum.sum()
end)
|> Enum.max()
end
end
I’m currently learning Elixir (only been at it for about 2 weeks), and I’ve been working on a problem from Codewars where I need to:
1-Split a string into substrings separated by vowels (aeiou).
2-Calculate a score for each substring based on the sum of alphabet values (e.g., a=1, b=2, ..., z=26).
3- Find the maximum score among the substrings.
defmodule Kata do
def solve(s) do
alphabet_values =
for {char, index} <- Enum.with_index(?a..?z, 1), into: %{}, do: {<<char::utf8>>, index}
vowels = "aeiou"
{substrings, last_segment} =
s
|> String.graphemes()
|> Enum.reduce({[], ""}, fn char, {acc, current} ->
if !Enum.any?(String.split(vowels, ""), fn x -> x == char end) do
{acc, current <> char}
else
{[current | acc], ""}
end
end)
substrings = [last_segment | substrings]
substrings
|> Enum.map(fn substring ->
substring
|> String.graphemes()
|> Enum.map(&alphabet_values[&1])
|> Enum.sum()
end)
|> Enum.max()
end
end
Everything can be calculated in one pass, using for
comprehension for binaries with a reduce:
option https://hexdocs.pm/elixir/Kernel.SpecialForms.html#for/1-the-reduce-option
for <<c <- "abcdefgh">>, reduce: {[[]], 0, 0} do
{[s | rest], max, curr} ->
if c in ~c'aeoiu',
do: {[[], s | rest], max(max, curr), 0},
else: {[[c | s] | rest], max, curr + c - ?a + 1}
end
{[~c"hgf", ~c"dcb", []], 9, 21}
The result would contain a tuple, having reversed splitted charlists, a leftover from the latest reduce, and a maximal value requested.
A lot of the parts you need are in the standard library.
Split a string into substrings separated by vowels
String.split/3
can do this in one call:
substrings = String.split(s, ["a", "e", "i", "o", "u"])
Calculate a score for each substring...
Enum.map/2
makes some calculation for each item in a list (or other enumerable) and returns the corresponding list of results.
...based on the sum of alphabet values (e.g., a=1, b=2, ..., z=26).
So given one of the substrings, you need to get a list of characters, then map each to its alphabet value. You've already found String.graphemes/1
which will break a string into a list of single-character strings; for this String.to_charlist/1
might be more convenient, since it returns a list of Unicode code points. From there you can again Enum.map
each code point to its alphabet value, and then Enum.sum/1
them together.
def score_for_substring(s) do
code_points = String.to_charlist(s)
alphabet_values = Enum.map(code_points, fn p -> p - ?a + 1 end)
Enum.sum(alphabet_values)
end
Find the maximum score among the substrings.
Is Enum.max/3
.
If I felt like gluing this all together into more compact functions, I could use pipelines:
defp substring_score(s) do
s
|> String.to_charlist()
|> Enum.map(&(&1 - ?a + 1))
|> Enum.sum()
end
def solve(s) do
s
|> String.split(["a", "e", "i", "o", "u"])
|> Enum.map(&substring_score/1)
|> Enum.max()
end
(Comparing this to the last block of code in the question, you're pretty close!)
It seems like this might be missing a step at the start to clear out any non-letter characters, both basic punctuation and non-Latin characters. I might for example String.replace(s, ~r/[^a-zA-Z]/, "")
to remove non-letters and then use String.downcase()
to convert remaning uppercase letters to lowercase.
Don't forget to write ExUnit test cases for this, especially when you can easily describe a set of input strings and expected scores and there are no external dependencies. I've found re-running mix test
generally quicker than trying to retype things into IEx as I'm iterating on a problem like this.
test "hello" do
assert Kata.solve("hello") == 24
end
test "world" do
assert Kata.solve("world") == 34
end