Если я имею дело с вложенными картами, первым делом, как правило, стоит подумать об обновлении или ассоциировании - для этого требуется последовательность вложенных ключей. Для такой проблемы, когда данные очень регулярны, это просто.
(assoc-in {} [1 "A" "item1"] 0.1)
;; =>
{1 {"A" {"item1" 0.1}}}
Использование последовательности во что-то другое, сокращение - идиоматический выбор. Редукционная функция находится прямо на краю уровня сложности, для которого я считаю анонимным fn, поэтому вместо этого я вытащу ее на место.
(defn- add-val [acc line]
(assoc-in acc [(:structure line) (:cat line) (:item line)] (:val line)))
(reduce add-val {} data)
;; =>
{1 {"A" {"item1" 0.1, "item2" 0.2}, "B" {"item3" 0.4}},
2 {"A" {"item1" 0.3}, "B" {"item3" 0.5}}}
Я думаю, это был тот эффект, который вы искали.
Меньше пройденных дорог:
Поскольку ваша последовательность поступает из базы данных, я не буду беспокоиться об использовании временной коллекции для ускорения агрегации. Кроме того, теперь я думаю об этом, так или иначе, работа с вложенными переходными картами - это боль.
update-in был бы полезен, если, например, вы хотите сложить какие-либо значения с одним и тем же ключом, но смысл вашего вопроса в том, что кортежи структуры / cat / item уникальны, и поэтому вам просто нужна группировка.
Juxt может быть использован для генерации структуры ключа - т.е.
((juxt :structure :cat :item) (first data))
[1 "A" "item1"]
но мне не ясно, есть ли способ использовать это, чтобы сделать add-val fn более читабельным.