我有一系列这样的地图:
({:autoNo "1" :title "Title1" :47652 ("name1") :47653 ("name2" "name3")} {:autoNo nil :title nil :47652 nil :47653 nil})
只有第一张地图包含文件上传字段键
:47652
和:47653
的值,而第二张地图中相同键的值是nil
。我想要的是文件上传字段键,每个地图只有一个文件值名称,如下所示:
({:autoNo "1" :title "Title1" :47652 "name1" :47653 "name2"} {:autoNo nil :title nil :47652 nil :47653 "name3"})
您可以在上面看到
:47652
在 seq
中只有一个值,因此它的值得到更新,而下一张地图中相同键的值仍然是 nil
。对于具有多个值的:47653
,第一个映射中的键包含第一个值,即"name2"
,而序列的第二个映射中的相同键包含下一个值,即"name3"
.
为了实现所以我创建了一个这样的函数:
(defn update-file-upload-field-values
[conversation-instances]
(let [file-field-ids [:47652 :47653]]
(first (map (fn [x]
(map
#(update %1 x (fn [z] (str %2)))
conversation-instances ((first conversation-instances) x)))
file-field-ids))))
其中
conversation-instances
与开头所述的地图序列相同。
我的解决方案没有按预期工作。它返回这个:
({:autoNo "1" :title "Title1" :47652 "name1" :47653 ("name2" "name3")})
这与开头显示的预期输出不同。
map
(conversation-instances
和((first conversation-instances) x)
的结果),所以结果将和它们中最短的一样长。我也认为你可以用update
替换
assoc
。
带有
map
的两个嵌套 first
也会产生一些您可能不想要的结果。我建议你一步一步地编写你的函数,在 REPL 中测试每个增量,这样你就知道你的函数是否达到了你的预期。
reduce
来解决这个问题。我的 update-maps
与您内心的 map
相似,我只是添加了 reduce
以针对每个给定的键重复它:
(def data '({:autoNo "1" :title "Title1" :47652 ("name1") :47653 ("name2" "name3")}
{:autoNo nil :title nil :47652 nil :47653 nil}))
(defn update-maps [coll k]
(let [values-seq (k (first coll))]
(mapv #(assoc %1 k %2)
coll
(concat values-seq (repeat nil)))))
(defn keys-fn [coll]
(let [ks [:47652 :47653]]
(reduce update-maps
coll
ks)))
(keys-fn data)
=>
[{:autoNo "1", :title "Title1", :47652 "name1", :47653 "name2"} {:autoNo nil, :title nil, :47652 nil, :47653 "name3"}]
(require '[com.rpl.specter :as s])
(use '[plumbing.core])
(let [data '({:autoNo "1" :title "Title1" :47652 ("name1") :47653 ("name2" "name3")}
{:autoNo nil :title nil :47652 nil :47653 nil})]
(->> data
(s/transform (s/subselect [s/ALL (s/keypath :47652) (s/nil->val [nil])]) aconcat)
(s/transform (s/subselect [s/ALL (s/keypath :47653) (s/nil->val [nil])]) aconcat)))
基本上,你可以做的是这样的:
(map assoc data
(repeat :47652)
(concat (:47652 (first data)) (repeat nil))
(repeat :47653)
(concat (:47653 (first data)) (repeat nil)))
;;=> ({:autoNo "1", :title "Title1", :47652 "name1", :47653 "name2"}
;; {:autoNo nil, :title nil, :47652 nil, :47653 "name3"})
为了摆脱重复,你可以这样做:
(defn ks-vs [[k vs]]
[(repeat k) (concat vs (repeat nil))])
(defn process [[head :as data]]
(->> (select-keys head [:47652 :47653])
(mapcat ks-vs)
(apply map assoc data)))
(process data)
;;=> ({:autoNo "1", :title "Title1", :47652 "name1", :47653 "name2"}
;; {:autoNo nil, :title nil, :47652 nil, :47653 "name3"})
这基本上是基于相同方法的更抽象的变体。
我不想回答除了被问到的问题之外的问题,但在你的问题中,列表中的第二张地图似乎是从第一张地图派生出来的,因此不需要。
还有许多(但不是全部)当前答案似乎是硬编码信息,即键或最大项目的长度。
我认为有3种解决方案:
话虽这么说,我将提供一个“懒惰”的解决方案,它既没有键也没有硬编码的长度:
(->> '{:autoNo "1" :title "Title1" :47652 ("name1") :47653 ("name2" "name3" "name4") :9999 ("foo9" "bar9")}
(mapcat (fn [[k v]]
[(repeat k)
(if (coll? v)
(concat v (repeat nil))
(cons v (repeat nil)))]))
(apply map hash-map)
(take-while #(some second %)))
应该产生类似的东西:
[{:47652 "name1" :47653 "name2" :9999 "foo" :autoNo "1" :title "Title1"}
{:47652 nil :47653 "name3" :9999 "bar" :autoNo nil :title nil}
{:47652 nil :47653 "name4" :9999 nil :autoNo nil :title nil}]