Clojure 矢量地图化
我有这个字段列表(这是Facebook的graph API字段列表) 我想从中生成一张地图。约定如下,如果在键向量之后,则其为键的内部对象。示例向量可以表示为地图,如下所示:Clojure 矢量地图化,clojure,Clojure,我有这个字段列表(这是Facebook的graph API字段列表) 我想从中生成一张地图。约定如下,如果在键向量之后,则其为键的内部对象。示例向量可以表示为地图,如下所示: {"a" "value" "b" {"c" {"t" "value"} "d" "value"} "e" {"f" "value"} "g" "value"} 到目前为止,我有这个解决方案 (defn traverse [data] (mapcat (fn [[left right]]
{"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"}
但这是脂肪和难以遵循。我想知道是否有更优雅的解决方案。这里有另一种方法,在整个遍历过程中使用
postwark
,而不是仅在默认值
替换时使用:
(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"}
这里有另一种方法,可以在整个遍历过程中使用
postwark
,而不是仅在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"}
试图阅读大量嵌套的代码会让我头疼。更糟糕的是,当答案是“强制配合”的时候,
postwark
,它以一种“由内而外”的方式做事。另外,使用分区all
有点浪费,因为我们需要丢弃任何具有两个非向量的对
对我来说,最自然的解决方案是简单的自顶向下递归。唯一的问题是,我们事先不知道是否需要从输入序列的头部删除一个或两个项目。因此,我们不能对循环或映射使用简单的
因此,只需将其编写为一个简单的递归,并使用if
来确定我们是从列表的开头消费1项还是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键
,只是为了让结果更加“Clojurey”。试图读取嵌套的代码会让我头疼。更糟糕的是,当答案是“强制配合”的时候,postwark
,它以一种“由内而外”的方式做事。另外,使用分区all
有点浪费,因为我们需要丢弃任何具有两个非向量的对
对我来说,最自然的解决方案是简单的自顶向下递归。唯一的问题是,我们事先不知道是否需要从输入序列的头部删除一个或两个项目。因此,我们不能对
循环或映射使用简单的
因此,只需将其编写为一个简单的递归,并使用if
来确定我们是从列表的开头消费1项还是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}
我在末尾添加了关键字
,只是为了让结果更“Clojurey”。因为你要求的是一个更干净的解决方案,而不是一个解决方案,因为我认为这是一个整洁的小问题,这里还有一个
(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)))
既然你要求的是一个更干净的解决方案,而不是一个解决方案,而且因为我认为这是一个整洁的小问题,这里还有一个
(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)))
这里有一个专门的网站,因为这里有一个专门的网站,我觉得@alan thompson的代码更容易阅读。也许只是因为它反映了我对这个问题的看法。没有comp
,->
,,解构,没有一行带有anon-fn和if
,所有这些都会给读者带来一些压力。就我个人而言,我觉得@alan thompson的代码更容易阅读。也许只是因为它反映了我对这个问题的看法。没有comp
,->
,,解构,没有一行带有anon-fn和if
,所有这些都会给读者带来一些压力。只是我谦逊的2ct。很好!非常简洁易懂!非常简洁易懂