Фильтрация перечислений: включение значения по индексу 1

#list #elixir

#Список #эликсир

Вопрос:

Я пытаюсь проанализировать флаги в стиле unix из args списка, т.Е.:

 args = ["hello", "-f", "val", "-u", "valtwo"]
# Desired outcome: [{"-f", "val"}, {"-u", "valtwo"}]
 

Мне удалось извлечь флаги с помощью следующего регулярного выражения:

 args
|> Enum.filter(fn arg -> Regex.match?(~r/^-{1,2}w /, arg) end)
# ["-f", "-u"]
 

Однако, чтобы получить значения, соответствующие этим ключам, мне нужно получить значение аргументов в следующем индексе.

Я рассматривал возможность преобразования массива с использованием Enum.chunk_every(2) (отбрасывая первый элемент, "hello" ), но это недостаточно гибко, поскольку это приведет к сбою в следующем:

 ["hello", "-f", "val", "secondvalforf", "-u", "valtwo"]
# Achieved outcome: [{"-f", "val"}, {"secondvalforf", "-u"}, {"valtwo"}]
 

Любые идеи приветствуются.

Ответ №1:

Enum.reduce/3 здесь твой друг.

 args = ~w|hello -f val foo -u valtwo|
#⇒ ["hello", "-f", "val", "foo", "-u", "valtwo"]

Enum.reduce(args, [], fn 
  <<?-, _>> = key, args -> [{key, []} | args]
  _, [] -> [] # skip plain arguments
  arg, [{key, last} | rest] -> [{key, last    [arg]} | rest]
end)
#⇒ [{"-u", ["valtwo"]}, {"-f", ["val", "foo"]}]
 

Вызов Enum.reverse/1 результата для сохранения порядка ключей ( reduce добавляет новые элементы в накопитель, чтобы избежать создания множества последующих связанных списков в будущем.)