Переводя вектор на карту - PullRequest
       1

Переводя вектор на карту

0 голосов
/ 20 сентября 2018

У меня есть этот список полей (это список полей API для Facebook).

["a" "b" ["c" ["t"] "d"] "e" ["f"] "g"]

Я хочу создать из него карту.Соглашение следующее: если после ключевого вектора следует, то это внутренний объект для ключа.Примерный вектор можно представить в виде карты в виде:

{"a" "value"
 "b" {"c" {"t" "value"} "d" "value"}
 "e" {"f" "value"}
 "g" "value"}

Итак, у меня пока есть это решение

(defn traverse
  [data]
  (mapcat (fn [[left right]]
            (if (vector? right)
              (let [traversed (traverse right)]
                (mapv (partial into [left]) traversed))
              [[right]]))
          (partition 2 1 (into [nil] data))))

(defn facebook-fields->map
  [fields default-value]
  (->> fields
       (traverse)
       (reduce #(assoc-in %1 %2 nil) {})
       (clojure.walk/postwalk #(or % default-value))))

(let [data ["a" "b" ["c" ["t"] "d"] "e" ["f"] "g"]]
  (facebook-fields->map data "value"))
#=> {"a" "value", "b" {"c" {"t" "value"}, "d" "value"}, "e" {"f" "value"}, "g" "value"}

Но оно жирное и за ним трудно следовать.Мне интересно, есть ли более элегантное решение.

Ответы [ 3 ]

0 голосов
/ 20 сентября 2018

Попытка прочитать сильно вложенный код вызывает у меня головную боль.Хуже, когда ответом является что-то вроде «принудительного соответствия» с postwalk, которое делает что-то вроде «наизнанку».Кроме того, использование partition-all является пустой тратой, поскольку нам нужно отбросить любые пары с двумя не векторами.

Для меня наиболее естественным решением является простая нисходящая рекурсия.Единственная проблема заключается в том, что мы не знаем заранее, нужно ли нам удалить один или два элемента из заголовка входной последовательности.Таким образом, мы не можем использовать простой цикл for или map.

Итак, просто напишите его как прямую рекурсию и используйте if, чтобы определить, потребляем ли мы 1 или 2 элемента изглава списка.

  1. Если второй элемент является значением, мы потребляем один элемент и добавляем :dummy-value, чтобы создать запись на карте.
  2. Если 2-й элемент является вектором, мы рекурсивно используем его в качестве значения в записи на карте.

Код:

(ns tst.demo.core
  (:require [clojure.walk :as walk] ))

(def data ["a" "b" ["c" ["t"] "d"] "e" ["f"] "g"])

(defn parse [data]
  (loop [result {}
         data   data]
    (if (empty? data)
      (walk/keywordize-keys result)
      (let [a (first data)
            b (second data)]
        (if (sequential? b)
          (recur
            (into result {a (parse b)})
            (drop 2 data))
          (recur
            (into result {a :dummy-value})
            (drop 1 data)))))))

с результатом:

(parse data) => 
{:a :dummy-value,
 :b {:c {:t :dummy-value}, :d :dummy-value},
 :e {:f :dummy-value},
 :g :dummy-value}

Я добавил keywordize-keys в конце, чтобы сделать результат немного более "Clojurey".

0 голосов
/ 27 сентября 2018

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

(defn facebook-fields->map [coll]
  (into {}
        (keep (fn [[x y]]
                (when-not (vector? x)
                  (if (vector? y)
                    [x (facebook-fields->map y)]
                    [x "value"]))))
        (partition-all 2 1 coll)))
0 голосов
/ 20 сентября 2018

Вот еще один способ сделать это, используя postwalk для всего обхода, вместо того, чтобы использовать его только для замены default-value:

(defn facebook-fields->map
  [fields default-value]
  (clojure.walk/postwalk
    (fn [v] (if (coll? v)
              (->> (partition-all 2 1 v)
                   (remove (comp coll? first))
                   (map (fn [[l r]] [l (if (coll? r) r default-value)]))
                   (into {}))
              v))
    fields))

(facebook-fields->map ["a" "b" ["c" ["t"] "d"] "e" ["f"] "g"] "value")
=> {"a" "value",
    "b" {"c" {"t" "value"}, "d" "value"},
    "e" {"f" "value"},
    "g" "value"}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...