简体   繁体   English

如何根据键名有条件地更新 Clojure map 中的键和值?

[英]How do I conditionally update keys and values in a Clojure map based on the key name?

I have a map and the keys are strings.我有一个 map,键是字符串。 If the key contains the word "kg" I want to multiply the value by 2.2 and then replace "kg" with "lb" in the key.如果密钥包含单词“kg”,我想将该值乘以 2.2,然后将密钥中的“kg”替换为“lb”。 I can't figure out how to iterate over the map in a way that I can conditionally update it.我不知道如何以可以有条件地更新它的方式迭代 map。

Example map:示例 map:

{"id" ("7215" "74777" "7219"),
 "weight-kg" ("150" "220" "530"),
 "time-seconds" ("1900" "2" "770")}

Desired output所需 output

{"id" ("7215" "74777" "7219"),
 "weight-lb" ("330" "485" "1168"),
 "time-seconds" ("1900" "2" "770")}

I've tried update , for map and reduce-kv .我试过update for mapreduce-kv Project requirement is to not use the string library, which is why there is re-find .项目需求是不使用字符串库,所以才会有re-find These are only attempts at changing the values.这些只是改变价值观的尝试。 Since I can't change the values, I haven't attempted changing the keys.由于我无法更改值,因此我没有尝试更改密钥。

(defn kg->lb [m k]
    (if (re-find #"kg" k)
      (map #(update m % * 2.2))))

(defn kg2->lb2 [m]
    (reduce-kv #(if (re-find #"kg" %)
                  (update % * 2.2)) {} m)

(map #(if (re-find #"kg" %)
        (update % * 2.2)) m)

(for [k (keys m)]
    (if (re-find #"kg" k)
      (update m k #(* % 2.2))))

Data:数据:

(def data {"id"           ["7215" "74777" "7219"],
           "weight-kg"    ["150" "220" "530"],
           "time-seconds" ["1900" "2" "770"]})

Helper function to convert a string (kg amount) to string (lb amount):助手 function 将字符串(kg 数量)转换为字符串(lb 数量):

(defn kg->lb [kg-string]
  (-> kg-string
      parse-long
      (* 2.2)
      int
      str))

The most important function is reduce-kv .最重要的 function 是reduce-kv

If you find "kg" in key, you will replace that with "lb" and map helper function over all values.如果您在键中找到"kg" ,您将用"lb"和 map helper function 替换所有值。

If you don't find "kg" in key, you will just assoc that entry without change.如果您在密钥中没有找到"kg" ,您将直接关联该条目而无需更改。

(reduce-kv (fn [m k v]
             (if (re-find #"kg" k)
               (assoc m (str/replace k #"kg" "lb")
                        (map kg->lb v))
               (assoc m k v)))
           {} 
           data)

I think I passed Project requirement is to not use the string library , except for (str/replace k #"kg" "lb") , which you can replace with String/replace interop: (.replace k "kg" "lb") .我想我通过了项目要求是不使用字符串库,除了(str/replace k #"kg" "lb") ,你可以用String/replace interop: (.replace k "kg" "lb")

EDIT: Solution with map and into :编辑:解决方案mapinto

(defn update-entry [[k v]]
  (if (re-find #"kg" k)
    [(.replace k "kg" "lb") (map kg->lb v)]
    [k v]))

(->> data
     (map update-entry)
     (into {}))

Transducer version:传感器版本:

(into {} (map update-entry) data)

I would do it a bit differently.我会做一些不同的。 First, I'd convert the strings to integers using the Java function Long/parseLong :首先,我使用 Java function Long/parseLong将字符串转换为整数:

(ns tst.demo.core
  (:use demo.core tupelo.core tupelo.test)
  (:require
    [tupelo.core :as t]
    ))

(defn parse-vec-longs
  [v]
  (mapv #(Long/parseLong %) v))

(verify
  (is= (parse-vec-longs ["150" "220" "530"])
    [150 220 530]))

Then I would write the code to convert the kg vals to lb vals.然后我会编写代码将 kg vals 转换为 lb vals。 At the end, just use dissoc to get rid of the kg data, then assoc to add in the new lb data:最后,只需使用dissoc去掉 kg 数据,然后assoc添加新的 lb 数据:

(defn convert-kg-lb
  [data]
  (let-spy-pretty [kg-vals (get data "weight-kg")
                   lb-vals (mapv #(Math/round (* 2.2 %)) kg-vals)
                   result  (t/it-> data
                             (dissoc it "weight-kg")
                             (assoc it "weight-lb" lb-vals))]
    result))

(verify
  (let [data-str        {"id"           ["7215" "74777" "7219"]
                         "weight-kg"    ["150" "220" "530"]
                         "time-seconds" ["1900" "2" "770"]}
        data-parsed     (t/map-vals data-str #(parse-vec-longs %))
        expected-parsed {"id"           [7215 74777 7219]
                         "weight-kg"    [150 220 530]
                         "time-seconds" [1900 2 770]}
        expected-out    {"id"           [7215 74777 7219]
                         "weight-lb"    [330 484 1166]
                         "time-seconds" [1900 2 770]}
        result          (convert-kg-lb data-parsed)]
    (is= data-parsed expected-parsed)
    (is= (spyx-pretty result) (spyx-pretty expected-out))))

Normally, you'd also replace all the string keys with keywords as well, so "lb" => :lb .通常,您还会用关键字替换所有字符串键,因此"lb" => :lb

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM