How Can I Optimize String Splitting and Scoring Logic in Elixir? (Codewars Problem) - Stack Overflow

admin2025-05-01  1

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
Share Improve this question edited Jan 3 at 5:05 Aleksei Matiushkin 121k12 gold badges108 silver badges171 bronze badges asked Jan 2 at 16:48 Mohamed Aziz OuerghiMohamed Aziz Ouerghi 11 silver badge2 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 1

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
转载请注明原文地址:http://www.anycun.com/QandA/1746106397a91759.html