HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Count words in string and tally up the repetitive words

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
thewordsrepetitivetallyandcountstring

Problem

I'm an elixir beginner. I'm doing the word count exercise of exercism website. The word count exercise returns a map with the word as the key and the number of instances as value. The exercise excludes special characters, and ignores underscores. My code works, but I'm sure there is a way to make it cleaner. I've attached the test file so that you can see the criteria that it needs to pass.

word_count.exs

defmodule Words do
  @doc """
  Count the number of words in the sentence.

  Words are compared case-insensitively.
  """
  @spec count(String.t) :: map
  def count(sentence) do
    String.downcase(sentence)                                           |>
      String.split(~r/[\s_]/)                                           |>
      Enum.filter(fn(word) -> String.match?(word, ~r/[a-zA-z\d]/) end)  |>
      Enum.map(fn(word) -> String.replace(word, ~r/[:!&@$%^,]/, "") end)|>
      Enum.group_by(fn(word) -> word end)                               |>
      Enum.reduce(%{}, fn({k, v}, acc) -> Map.put(acc, k, Enum.count(v)) end)
  end
end


word_count_test.exs

```
if !System.get_env("EXERCISM_TEST_EXAMPLES") do
Code.load_file("word_count.exs")
end

ExUnit.start
ExUnit.configure exclude: :pending, trace: true

defmodule WordsTest do
use ExUnit.Case

test "count one word" do
assert Words.count("word") == %{ "word" => 1 }
end

test "count one of each" do
expected = %{ "one" => 1 , "of" => 1 , "each" => 1 }
assert Words.count("one of each") == expected
end

test "count multiple occurrences" do
expected = %{ "one" => 1 , "fish" => 4 , "two" => 1 , "red" => 1 , "blue" => 1 }
assert Words.count("one fish two fish red fish blue fish") == expected
end

test "ignore punctuation" do
expected = %{"car" => 1, "carpet" => 1, "as" => 1, "java" => 1, "javascript" => 1}
assert Words.count("car : carpet as java : javascript!!&@$%^&") == expected
end

test "include numbers" do
expected = %{"testing" => 2, "1" => 1,

Solution

Just finished that exercise too.
I think you can extract some of that logic to anonymous functions.
It isolates a pieces of behaviour that you can work on.

defmodule Words do
  @doc """
  Count the number of words in the sentence.

  Words are compared case-insensitively.
  """
  @spec count(String.t) :: map
  def count(sentence) do
    filter_word = fn(word) ->
      String.match?(word, ~r/[a-zA-z\d]/)
    end

    remove_special_characters = fn(word) ->
      String.replace(word, ~r/[:!&@$%^,]/, "")
    end

    count_words = fn({k, v}, acc) ->
      Map.put(acc, k, Enum.count(v))
    end

    sentence
    |> String.downcase
    |> String.split(~r/[\s_]/)
    |> Enum.filter(filter_word)
    |> Enum.map(remove_special_characters)
    |> Enum.group_by(fn(word) -> word end)
    |> Enum.reduce(%{}, count_words)
  end

end


And actually, I've created a function that returns a list of words to count: http://exercism.io/submissions/e26667aff3734da79ac0b6c6fedfd3da

Hope that helps!

Code Snippets

defmodule Words do
  @doc """
  Count the number of words in the sentence.

  Words are compared case-insensitively.
  """
  @spec count(String.t) :: map
  def count(sentence) do
    filter_word = fn(word) ->
      String.match?(word, ~r/[a-zA-z\d]/)
    end

    remove_special_characters = fn(word) ->
      String.replace(word, ~r/[:!&@$%^,]/, "")
    end

    count_words = fn({k, v}, acc) ->
      Map.put(acc, k, Enum.count(v))
    end

    sentence
    |> String.downcase
    |> String.split(~r/[\s_]/)
    |> Enum.filter(filter_word)
    |> Enum.map(remove_special_characters)
    |> Enum.group_by(fn(word) -> word end)
    |> Enum.reduce(%{}, count_words)
  end

end

Context

StackExchange Code Review Q#129583, answer score: 2

Revisions (0)

No revisions yet.