[英]how to update-in ingoring the first level in clojure
使用update-in时,我们需要提供元素的完整路径。 但是,如果我想更新第二级键为:MaxInclude的所有元素怎么办?
例如输入是
(def a {:store {:type "varchar"},
:amount {:nullable true, :length nil, :type "float", :MaxInclude "100.02"},
:unit {:type "int"},
:unit-uniform {:type "int" :MaxInclude "100"}
})
所需的输出是(根据theie类型将MaxInclude从字符串转换为float / int):
{:store {:type "varchar"},
:amount {:nullable true, :length nil, :type "float", :MaxInclude 100.02},
:unit {:type "int"},
:unit-uniform {:type "int" :MaxInclude 100}
}
我当时想拥有一个像update-in
这样的功能会很不错,它可以匹配键谓词功能而不是确切的键值。 这是我想出的:
(defn update-all
"Like update-in except the second parameter is a vector of predicate
functions taking keys as arguments. Updates all values contained at a
matching path. Looks for keys in maps only."
[m [key-pred & key-preds] update-fn]
(if (map? m)
(let [matching-keys (filter key-pred (keys m))
f (fn [acc k]
(update-in acc [k] (if key-preds
#(update-all %
key-preds
update-fn)
update-fn)))]
(reduce f m matching-keys))
m))
完成此操作后,您需要做的是:
(update-all a [= #{:MaxInclude}] read-string)
=
用作第一个键匹配函数,因为传递一个参数时,它始终返回true。 第二个事实是集合是一个函数。 此函数使用非优化的递归,但调用堆栈的深度仅与匹配的映射级别的数目相同。
(into {}
(map (fn [[k v]]
{k (if (contains? v :MaxInclude)
(update-in v [:MaxInclude] read-string)
v)})
a))
在这里,我正在映射键值对,并将每个键值对分解为k和v。然后update-in
如果值包含:MaxInclude
,则对值使用update-in
。 最后,我倒从列表中对into
哈希映射。
笔记:
contains?
如果主地图的任何值都不是索引集合。 read-string
作为将字符串转换为数字的便捷方法,就像Clojure阅读器在编译作为数字文字的字符串时所做的一样。 这种方法可能有缺点。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.