根据第一张地图的键值更新一系列地图中的键值

问题描述 投票:0回答:4

我有一系列这样的地图:

({: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")})

这与开头显示的预期输出不同。

clojure hashmap
4个回答
0
投票

你用两个序列调用

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"}]

0
投票
(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)))

0
投票

基本上,你可以做的是这样的:

(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"})

这基本上是基于相同方法的更抽象的变体。


0
投票

我不想回答除了被问到的问题之外的问题,但在你的问题中,列表中的第二张地图似乎是从第一张地图派生出来的,因此不需要。

还有许多(但不是全部)当前答案似乎是硬编码信息,即键或最大项目的长度。

我认为有3种解决方案:

  1. 循环重复
  2. 惰性映射
  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}]
© www.soinside.com 2019 - 2024. All rights reserved.