Want to get the updated values of list a, but instead I got empty list (Elixir)

“”"
string_b = ~w(cashregister Carthorse radishes)
a = []
Enum.filter(string_b, fn x →
word = x
|> String.downcase()
|> String.graphemes()
|> Enum.sort()

  a = word ++ a
end)

IO.inspect(a)
“”"

string_b is a list of strings. I want to get the updated strings in a.

Please surround your code with triple backticks, otherwise it is really hard to read it.

Please check now.

you want to use ```

string_b = ~w(cashregister Carthorse radishes)
a = []

Enum.filter(string_b, fn x ->
  word =
    x
    |> String.downcase()
    |> String.graphemes()
    |> Enum.sort()

  a = word ++ a
  a = []

  Enum.filter(string_b, fn x ->
    word =
      x
      |> String.downcase()
      |> String.graphemes()
      |> Enum.sort()

    a = word ++ a
    IO.inspect(a)

    word == original_string
  end)
end)

what is original_string ?
You should create a module and functions, and assign the return of those functions to your variables. There is code repetition, so you could abstract them into functions.

original_string is a user input string value. In this case let’s assume original_string = “orchestra”

Here is my full code.

defmodule Anagram do
  def match(string_a, string_b) do
    original_string =
      string_a
      |> String.downcase()
      |> String.graphemes()
      |> Enum.sort()

    a = []
    Enum.filter(string_b, fn x ->
      word = x
      |> String.downcase()
      |> String.graphemes()
      |> Enum.sort()
     
      a = word ++ a
      IO.inspect(a)

      word == original_string
    end)

  end
end

IO.inspect(Anagram.match("Orchestra", ~w(cashregister carthorse radishes)))

Hope so you understand now.

Please, explain in plain English what’s does your algorithm do.

Nope, because your code needs to be properly formatted. There is an option in the visual editor, plus you can preview how it will look.

function match takes 2 arguments, first is string and 2nd is list that contain strings.

Then I split first argument and sort alphabetically.

After that I am using Enum.filter in the second argument to check the anagram of first argument. If it is present then it should return that string otherwise it will return an empty string. But there is one condition I need to apply here which is if there are multiple anagrams present in the 2nd argument (list of strings) then I should not return that string instead I should return an empty list.

That is why I initialize an empty list which is a = []. to check if there are multiple anagrams available for the string which is first argument. But I do not know how to append values in the list a. So that when I access the values of list a after Enum.filter scope I got all the strings in a to check if there are multiple appearances of the first argument in the form of Anagram.

Please help if you know how to do this.
Thanks.

I did it now. Sorry it is my first time that is why don’t have an idea how to post questions.

Sorry, I don’t quiet follow the explanation of your code. My question was referring to what your algorithm should do, not what your code does or ir trying to do.

Maybe this is something similar to what you need, without covering the edge-cases that you mention.

normalize = fn(string) ->
  string
  |> String.downcase()
  |> String.graphemes()
  |> Enum.sort()
  |> List.to_string()
end

words = ~w(cashregister Carthorse radishes rochestra)
original_word = normalize.("Orchestra")

{_, result} = 
  Enum.reduce(words, {original_word, []}, fn word, {original_word, acc} ->
    if original_word == normalize.(word) do
      {original_word, [word | acc]}
    else
      {original_word, acc}
    end
  end)

IO.inspect(result)

that code will return ["rochestra", "Carthorse"]

Given a word and a list of possible anagrams, select the correct sublist.

Given “listen” and a list of candidates like “enlists” “google” “inlets” “banana” the program should return a list containing “inlets”.

Also if there are multiple anagrams present in the list it should return an empty list.

i.e

test "does not detect an anagram if the original word is repeated" do
    matches = Anagram.match("go", ~w(go Go GO))
    assert matches == []
  end
1 Like

Please have a look

defmodule Anagram do
  @spec normalize(String.t) :: String.t
  def normalize(string) when is_binary(string) do
    string
    |> String.downcase()
    |> String.graphemes()
    |> Enum.sort()
    |> List.to_string()
  end
  
  @spec match(String.t, []) :: []
  @spec match(String.t, nonempty_list(String.t)) :: [String.t]
  def match(string, words) when is_binary(string) and is_list(words) do
    original_word = normalize(string)

    {result, _} =
      Enum.reduce_while(words, {[], original_word},
        fn word, {acc, original_word} when length(acc) <= 1 and is_binary(word) ->
          if original_word == normalize(word) do
            case acc do
              [] -> {:cont, {[word], original_word}}
              [_] -> {:halt, {[], original_word}}
            end
          else
            {:cont, {acc, original_word}}
          end

          _word, {_acc, original_word} ->
            {:halt, {[], original_word}}
        end)

    result
  end
end

iex> IO.inspect(Anagram.match("Orchestra", ~w(cashregister carthorse radishes)))
["carthorse"]

iex> IO.inspect(Anagram.match("go", ~w(go Go GO)))
[]
2 Likes

Thanks for your help.

1 Like

You are welcome.
It may look complicated, but that is how you avoid to unnecessarily traverse a list in Elixir.

If you want to know how to add elements to a list, there are two ways:
one is the with cons operator [:new_element | list] another one is the the ++/2 operator. Kernel — Elixir v1.12.3
We don’t use that in this case, because you are only interested in lists of one element.

Read more about:

2 Likes

Yeah I am reading about this. Thanks again.

1 Like

If you have questions about the code, feel free to ask. Or even better if you have alternatives/improvements.

I have added guards and specs to the example.

1 Like