Сглаживание карты путем соединения клавиш

Учитывая вложенную карту с ключевыми словами, такими как {:foo {:bar 1 :baz [2 3] :qux {:quux 4}} :corge 5}, как я могу реализовать flatten-map, так что (flatten-map {:foo {:bar 1 :baz [2 3] :qux {:quux 4}} :corge 5} "-") создает нечто вроде {:foo-bar 1 :foo-baz [2 3] :foo-qux-quux 4 :corge 5}.

Моя лучшая попытка:

(defn flatten-map
  ([form separator] (flatten-map form separator nil))
  ([form separator prefix]
  (if (map? form)
    (into {} (map (fn [[k v]]
                    [(keyword (str prefix (name k)))
                     (flatten-map v separator (str prefix (name k) separator))])
                  form))
    form)))

Как вы можете видеть, я не могу получить flatten-map, чтобы выбрать только "листья".

Ответ 1

(defn flatten-map
  ([form separator]
     (into {} (flatten-map form separator nil)))
  ([form separator pre]
     (mapcat (fn [[k v]]
               (let [prefix (if pre (str pre separator (name k)) (name k))]
                 (if (map? v)
                   (flatten-map v separator prefix)
                   [[(keyword prefix) v]])))
               form)))

вы безоговорочно создавали новые пары ключ/значение, даже когда значение должно было быть расширено, поэтому я переключил карту на mapcat, чтобы результат мог быть "включен" на верхний уровень (это также потребовало разделения (into {} ...) в верхнюю версию формы, так как на самом деле мы не хотим, чтобы какие-либо карты нигде, кроме верхнего уровня вывода).

Вот как это работает с вашим примером:

user> (flatten-map {:foo {:bar 1 :baz [2 3] :qux {:quux 4}} :corge 5} "-")
{:foo-bar 1, :foo-qux-quux 4, :foo-baz [2 3], :corge 5}