我正在尝试解决问题:我需要从传入的值创建一个映射,但是当值的符号名称一致时,它们映射到的键不是。例如:我可能会传递一个用户ID的值。在代码中,我始终可以使用符号user-id
- 但根据其他因素,我可能需要制作地图{"userId" user-id}
或{"user_id" user-id}
或{:user-id user-id}
或 - 好吧,你明白了。
我可以写一个让我分道扬的宏:
(defmacro user1 [user-id] `{"userId" ~user-id}
(defmacro user2 [user-id] `{"user_id" ~user-id}
但我宁愿做的是定义一组地图,然后将它们与一组给定的符号组合:
(def user-id-map-1 `{"userId" `user-id}
(defn combiner [m user-id] m) ;; <-- Around here, a miracle occurs.
我无法弄清楚如何进行此评估。似乎我应能够制作包含未评估符号的地图,然后在函数或宏的词法范围内查找这些符号,将这些符号绑定为本地符号 - 但如何
答案 0 :(得分:2)
使用带有标准关键字密钥的地图,而不是标准化您的符号名称。你不需要靠近宏,你可以把你的地图变成记录,如果需要没有太多麻烦。
你所知道的
(def user1 {:id 3124, :surname "Adabolo", :forenames ["Julia" "Frances"]})
...可以通过使用您选择的任何函数映射键来进行转换:
(defn map-keys [keymap m]
(zipmap (map keymap (keys m)) (vals m)))
例如,
(map-keys name user1)
;{"id" 3124, "surname" "Adabolo", "forenames" ["Julia" "Frances"]}
或
(map-keys {:id :user-id, :surname :family-name} user1)
;{:user-id 3124, :family-name "Adabolo", nil ["Julia" "Frances"]}
如果要删除nil
条目,请将表达式包装在(dissoc ... nil)
中:
(defn map-keys [keymap m]
(dissoc
(zipmap (map keymap (keys m)) (vals m))
nil))
然后
(map-keys {:id :user-id, :surname :family-name} user1)
;{:user-id 3124, :family-name "Adabolo"}
我从具有优先权的Michał Marczyk's answer看到,上面基本上重写了clojure.set/rename-keys
,但是...... {/ p>
例如,
(clojure.set/rename-keys user1 {:id :user-id, :surname :family-name})
;{:user-id 3124, :forenames ["Julia" "Frances"], :family-name "Adabolo"}
例如,
(clojure.set/rename-keys user1 name)
;IllegalArgumentException Don't know how to create ISeq from: clojure.core$name ...
如果您放弃使用false
和nil
作为键,则可以保持丢失的键不变,仍然使用普通功能:
(defn map-keys [keymap m]
(zipmap (map #(or (keymap %) %) (keys m)) (vals m)))
然后
(map-keys {:id :user-id, :surname :family-name} user1)
;{:user-id 3124, :family-name "Adabolo", :forenames ["Julia" "Frances"]}
答案 1 :(得分:1)
如何将传入的值放入由正式参数名称伪造的关键字键入的地图中:
(defmacro zipfn [map-name arglist & body]
`(fn ~arglist
(let [~map-name (zipmap ~(mapv keyword arglist) ~arglist)]
~@body)))
使用示例:
((zipfn argmap [x y z]
argmap)
1 2 3)
;= {:z 3, :y 2, :x 1}
更好的是,不要使用宏:
;; could take varargs for ks (though it would then need another name)
(defn curried-zipmap [ks]
#(zipmap ks %))
((curried-zipmap [:x :y :z]) [1 2 3])
;= {:z 3, :y 2, :x 1}
然后您可以使用clojure.set/rename-keys
重新输入此地图:
(clojure.set/rename-keys {:z 3, :y 2, :x 1} {:z "z" :y "y" :x "x"})
;= {"x" 1, "z" 3, "y" 2}
这里的第二张地图是&#34;翻译地图&#34;为了钥匙;你可以通过合并像{:x "x"}
这样的地图来构建,描述如何重命名各个键。
答案 2 :(得分:0)
对于您描述的问题,我找不到使用宏的原因。
我推荐像
这样的东西(defn assoc-user-id
[m user-id other-factors]
(assoc m (key-for other-factors) user-id))
您实施key-for
的位置,以便根据other-factors
选择密钥。