Выпрямить данные
В кложурном чатике скинули очередную задачу: дана такая-то мапа, нужно перегнать ее в другую мапу. Примерный исходник:
(var DATA
{:kek {:foo {:type :LOL}
:bar {:type :KEK}}
:owo {:pip {:type :AGA}}})
Ожидание:
{:LOL {:foo :kek}
:KEK {:bar :kek}
:AGA {:pip :owo}}
Это очень упрощенный пример. Грубо говоря, нужно вывернуть вложенную мапу наизнанку: поместить наверх то, что сейчас внизу.
Большинство кложуристов подходят к этой задаче как есть. Они берут мапу и
прогоняют ее через комбо map
, mapcat
и анонимных функций. Кто-то накручивает
reduce
внутри reduce
. В целом работает, но как это читать и отлаживать — я
не знаю.
Я тоже скинул решение, точнее его часть, и оно зашло: понаставили пальчиков и огней. Раз так, стоит рассказать подробней, тем более что я замечаю, что никто так не делает.
Итак, если посмотреть на исходную мапу, станет ясно, что когда-то она была плоской таблицей:
kek foo LOL
kek bar KEK
owo pip AGA
Из-за того, что в левой части были повторы, кто-то решил избавиться от них группировкой. Но данные никуда не пропали: мы по-прежнему знаем, у кого какой атрибут. Посмотрев на самый вложенный элемент, легко проследить его путь. Например, обнаружить, что AGA начинается с owo.
Так вот, чтобы переколбасить эту мапу во что-то другое, нужно сперва выправить данные — привести их к таблице. Поможет макрос for. Он принимает несколько коллекций и строит декартово произведение их элементов. Примечательно, что каждая следующая коллекция может быть получена из предыдущих элементов. Код ниже строит ленивую таблицу:
(for [[k1 submap1]
DATA
[k2 submap2]
submap1
[_ item]
submap2]
[k1 k2 item])
([:kek :foo :LOL]
[:kek :bar :KEK]
[:owo :pip :AGA])
Теперь когда мапа развернута, можно сгруппировать ее по-другому. Как именно — это уже тривиальное дело, потому что, имея плоские данные, это делается на раз-два. Кроме того, нужно спросить себя — действительно ли нужна новая группировка? Может быть, лучше оставить как есть? Возможно, на той стороне тоже хотели бы плоские данные.
Я как-то рассказывал о нелепой ситуации со вложенностью. На одной стороне человек потеет, чтобы построить из списка вложенную мапу вида:
{:node shit
:children
[{:node crap
:children
[{:node fuck
:children
[...]}]}]}
А на второй стороне другой человек потеет, чтобы обойти ее как список. Оба пишут быдлокод и матерятся, а ради чего — не ясно. Такую структуру даже не каждый кложурист обойдет, потому что не все знают про tree-seq и зипперы.
Возвращаясь к теме переколбаса данных: хорошо бы запомнить аналогию. Плоские данные — это как лист бумаги, из которого можно сложить самолет, кораблик или лягушку-квакушку. Но переход от одной фигуры к другой происходит через развертку — то есть откату к нулевому состоянию. Только потом можно переходить к новому.
Наверняка найдется японец, который может сложить самолет в лягушку, минуя лист. Точно так же можно написать быдлокод, который переколбасит одну мапу в другую. Но это сложно, не очевидно, хрупко. Лучше избегать.
Видеть простейшую форму и возвращаться к ней, когда что-то идет не так — важный навык.
Нашли ошибку? Выделите мышкой и нажмите Ctrl/⌘+Enter