bazybaz
How to get a map with its key being a tuple matching the count of all characters and its value being a list of anagrams?
Basically, what I’m trying to do is that I want a map where the key is a tuple containing count of all same characters in the list and its values being the list of anagrams. To explain it in more clearer terms, I want the following:
%{{0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0} => ['eggs', 'gegs'], {1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0} => ['clay', 'alcy']}
Now, currently what I am getting is the following:
%{ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0} => "y", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0} => "s", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} => "l", {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} => "g", {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} => "e", {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} => "c", {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} => "a" }
I basically got this by writing the following code:
defmodule Anagram do
def traverse(strings, acc \\ Map.new()) do
Enum.reduce(strings, acc, fn string, acc ->
string
|> String.graphemes()
|> Enum.reduce(acc, &Anagram.fun/2)
end)
end
def fun(char, acc) do
{index, _} = :binary.match("abcdefghijklmnopqrstuvwxyz", char)
count = List.duplicate(0, 26)
count = List.update_at(count, index, &(&1 + 1))
tupcount = List.to_tuple(count)
Map.update(acc, tupcount, count, fn _ -> char end)
end
end
IO.inspect Anagram.traverse ["eggs", "gegs", "clay", "alcy"]
Any idea what needs to be corrected here? I’m pretty new to Elixir and experimenting with different stuff but I’m a little lost over here. Any help will be appreciated.
Most Liked
moogle19
You could map over the graphemes to get a list of matching indexes and then reduce it to the tuple like this:
def fun(string) do
string
|> String.graphemes()
|> Enum.map(fn char ->
{index, _} = :binary.match("abcdefghijklmnopqrstuvwxyz", char)
index
end)
|> Enum.reduce(List.duplicate(0, 26), fn index, list ->
List.update_at(list, index, &(&1 + 1))
end)
|> List.to_tuple()
end
After then you can reduce it to a map:
def traverse(strings) do
strings
|> Enum.reduce(%{}, fn string, map ->
key = fun(string)
Map.update(map, key, [string], fn list -> list ++ [string] end)
end)
end
ChristianAlexander
I took a pass at this code and extracted the key generation into a calculate_key function. Then moved map updates into the traverse function body.
defmodule Anagram do
def traverse(strings, acc \\ Map.new()) do
Enum.reduce(strings, acc, fn string, acc ->
# Update the map for each string, appending to the existing list
# if there is already a word with a matching key.
Map.update(acc, calculate_key(string), [string], &[string | &1])
end)
end
# Create a tuple from the character counts of a given word
def calculate_key(word) do
word
|> String.graphemes()
|> Enum.reduce(List.duplicate(0, 26), fn char, acc ->
{index, _} = :binary.match("abcdefghijklmnopqrstuvwxyz", char)
List.update_at(acc, index, &(&1 + 1))
end)
|> List.to_tuple()
end
end
IO.inspect(Anagram.traverse(["eggs", "gegs", "clay", "alcy"]))
Edit: looks like @moogle19 beat me to the implementation—glad to see that someone else would approach the problem in a similar way!







