type1fool
Alphabet Iterator
Hello, Elixir gang. I’m starting to get the hang of Elixir, which has been challenging since I’m not coming from Ruby. I have been struggling to find a solution for a particular problem. I need to generate a list of strings which increments over the alphabet. So["A", "B", "C"] is a very basic example.
When the end of the alphabet is reached, I want the result to look like this: [..."X", "Y", "Z", "AA", "BB"...]. The goal is to create a function like this: generate_list(first_letter, count).
I tried using the Alphabetify package, but it was pretty slow, and didn’t quite give me the results I needed. String.duplicate got me almost there, but I still wasn’t getting the right output.
Does anyone have an idea or package suggestion for this problem? I’ve hit a wall on this one.
Where I’m at ![]()
letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |> String.split("", trim: true)
def generate_list(first_letter, count)
when is_binary(first_letter)
when is_integer(count) do
# letters
# |> Stream.cycle
# |> Stream.take(count)
starting_index = letters |> Enum.find_index(fn x -> x == first_letter end)
# 0..count
# |> Enum.map(fn x -> end)
end
Marked As Solved
blatyo
This should do it. The first_letter part isn’t super efficient, but the rest should be.
EDIT: Fix divisor
def generate_list(first_letter \\ "A", count) when is_binary(first_letter) and is_integer(count) do
?A..?Z
|> Enum.map(&to_string([&1]))
|> Stream.cycle()
|> Stream.with_index()
|> Stream.map(fn {string, index} ->
String.duplicate(string, div(index, 26) + 1)
end)
|> Stream.drop_while(&(&1 != first_letter))
|> Enum.take(count)
end
Also Liked
michalmuskala
The fastest I could come up with is:
defmodule DirectIteration do
def list(from \\ "A", count) when is_binary(from) and is_integer(count) and count >= 0 do
char = :binary.at(from, 0)
len = byte_size(from)
build_list(char, len, count)
end
defp build_list(_char, _len, 0), do: []
defp build_list(?Z, len, count) do
[String.duplicate(<<?Z>>, len) | build_list(?A, len + 1, count - 1)]
end
defp build_list(char, len, count) do
[String.duplicate(<<char>>, len) | build_list(char + 1, len, count - 1)]
end
end
It’s about twice as fast as the LettersAndNumbers implementation.
Also - when you benchmark, if you get low IPS and short benchmark time, this means it run the code only once - this is not a good measurement. Try increasing the benchmark time, so that the iteration runs at least couple times.
kelvinst
That’s indeed a really good solution. But I got interested by the possibility of improving its performance and here are the results:
defmodule LettersAndNumbers do
def list(from \\ 0, count) do
from
|> stream()
|> Enum.take(count)
end
def stream(from \\ 0)
def stream(from) when is_bitstring(from) or is_list(from) do
from
|> letters_to_number()
|> stream()
end
def stream(from) do
from
|> Stream.iterate(&(&1 + 1))
|> Stream.map(&number_to_letters/1)
end
def number_to_letters(number) do
[rem(number, 26) + 65]
|> to_string()
|> String.duplicate(div(number, 26) + 1)
end
def letters_to_number(letters) when is_bitstring(letters) do
letters
|> String.to_charlist()
|> letters_to_number()
end
def letters_to_number([char | _] = letters) do
((length(letters) - 1) * 26) + (char - 65)
end
end
PS.: I also did some changes to the API, but basically, instead of cycling through the alphabet and navigating it with an index, I mathematically transform numbers into letters by adding 65 to them and duplicating them div(number, 26) + 1 times like you did.
type1fool
This is great, though it’s duplicating Z on the first pass.
iex(11)> generate_list("W", 10)
["W", "X", "Y", "ZZ", "AA", "BB", "CC", "DD", "EE", "FF"]
I changed div(index, 25) to div(index, 26) and the problem is solved. It correctly appends additional characters too:
iex(15)> generate_list("W", 10)
["W", "X", "Y", "Z", "AA", "BB", "CC", "DD", "EE", "FF"]
iex(20)> generate_list("ZZ", 60)
["ZZ", "AAA", "BBB", "CCC", "DDD", "EEE", "FFF", "GGG", "HHH", "III", "JJJ", "KKK", "LLL", "MMM", "NNN", "OOO", "PPP", "QQQ", "RRR", "SSS", "TTT", "UUU", "VVV", "WWW", "XXX", "YYY", "ZZZ", "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLL", "MMMM", "NNNN", "OOOO", "PPPP", "QQQQ", "RRRR", "SSSS", "TTTT", "UUUU", "VVVV", "WWWW", ...]
Thank you @blatyo for this clean solution!
Last Post!
type1fool
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex









