使用模式将Clojure地图条目提取到地图列表中?

sve*_*hie 0 dictionary functional-programming clojure

我有这样的地图(有1个或多个项目混合在一起):

{:item_name_1 "Great Deal"
 :item_options_2 "blah: 2"
 :item_name_2 "Awesome Deal" 
 :item_options_1 "foo: 3" 
 :item_quantity_1 "1"
 :item_price_2 "9.99" 
 :item_price_1 "9.99"
 :itemCount "2"}
Run Code Online (Sandbox Code Playgroud)

我想把它变成这样:

[{:item_quantity "1"
  :item_options "blah" 
  :item_name "Great Deal"
  :item_price "9.99"}
 {:item_name "Awesome Deal" 
  :item_options "foo"
  :item_quantity "1" 
  :item_price "9.99"}]
Run Code Online (Sandbox Code Playgroud)

所以,我想通过项目键将它们分开:

(def item-keys [:item_name :item_options :item_price :item_quantity])
Run Code Online (Sandbox Code Playgroud)

我猜我可能会以某种方式使用map或者walk我看不到该怎么做 - 我对Clojure很新.

我先说

(defn parse-items
  [mixed-map]
  (let [num-items (Integer/parseInt (:itemCount mixed-map))]
    (into []
      (do-something mixed-map))))
Run Code Online (Sandbox Code Playgroud)

tno*_*oda 5

我猜这个问题可以重新定义如下.

  1. 按关键字后缀在给定地图中对键值对进行分组.
  2. 为每个分组创建地图并将它们倒入新的矢量中.

如果这些假设是正确的,这是我的解决方案.

首先,定义一个名为的辅助函数,kv->skv它将原始键值对([k v])转换为后缀的向量和修改后的键值对([suffix [k' v]).

user> (def items {:item_name_1 "Great Deal"
                  :item_options_2 "blah: 2"
                  :item_name_2 "Awesome Deal" 
                  :item_options_1 "foo: 3" 
                  :item_quantity_1 "1"
                  :item_price_2 "9.99" 
                  :item_price_1 "9.99"
                  :itemCount "2"})
#'user/items

user> (defn- kv->skv
        [[k v]]
        (let [[_ k' s] (re-find #"(.+)_(\d+)" (name k))]
          [s [k' v]]))
#'user/kv->skv

user> (def items' (map kv->skv items))
#'user/items'

user> (clojure.pprint/pprint items')
(["1" ["item_name" "Great Deal"]]
 ["2" ["item_options" "blah: 2"]]
 ["2" ["item_name" "Awesome Deal"]]
 ["1" ["item_options" "foo: 3"]]
 ["1" ["item_quantity" "1"]]
 ["2" ["item_price" "9.99"]]
 ["1" ["item_price" "9.99"]]
 [nil [nil "2"]])
nil
Run Code Online (Sandbox Code Playgroud)

然后,使用项目键过滤项目.

user> (def item-keys #{:item_name :item_options :item_price :item_quantity})
#'user/item-keys

user> (def items-filtered (filter (comp item-keys keyword first second) items'))
#'user/items-filtered

user> (clojure.pprint/pprint items-filtered)
(["1" ["item_name" "Great Deal"]]
 ["2" ["item_options" "blah: 2"]]
 ["2" ["item_name" "Awesome Deal"]]
 ["1" ["item_options" "foo: 3"]]
 ["1" ["item_quantity" "1"]]
 ["2" ["item_price" "9.99"]]
 ["1" ["item_price" "9.99"]])
nil
Run Code Online (Sandbox Code Playgroud)

其次,使用该group-by函数按后缀对组修改键值对.

user> (def groupings (group-by first items-filtered))
#'user/groupings

user> (clojure.pprint/pprint groupings)
{"1"
 [["1" ["item_name" "Great Deal"]]
  ["1" ["item_options" "foo: 3"]]
  ["1" ["item_quantity" "1"]]
  ["1" ["item_price" "9.99"]]],
 "2"
 [["2" ["item_options" "blah: 2"]]
  ["2" ["item_name" "Awesome Deal"]]
  ["2" ["item_price" "9.99"]]]}
nil
Run Code Online (Sandbox Code Playgroud)

并将分组转换为地图.

user> (def what-you-want (->> (vals groupings)
                              (map #(->> %
                                         (map second)
                                         (into {})))))
#'user/what-you-want

user> (clojure.pprint/pprint what-you-want)
({"item_name" "Great Deal",
  "item_options" "foo: 3",
  "item_quantity" "1",
  "item_price" "9.99"}
 {"item_options" "blah: 2",
  "item_name" "Awesome Deal",
  "item_price" "9.99"})
nil
Run Code Online (Sandbox Code Playgroud)

最后,将这些步骤集成到一个函数中.

(defn extract-items
  [items item-keys]
  (let [kv->skv (fn
                  [[k v]]
                  (let [[_ k' s] (re-find #"(.+)_(\d+)" (name k))]
                    [s [k' v]]))]
    (->> items
         (map kv->skv)
         (filter (comp item-keys keyword first second))
         (group-by first)
         vals
         (map #(->> %
                    (map second)
                    (into {}))))))
Run Code Online (Sandbox Code Playgroud)

有用.

user> (clojure.pprint/pprint (extract-items items item-keys))
({"item_name" "Great Deal",
  "item_options" "foo: 3",
  "item_quantity" "1",
  "item_price" "9.99"}
 {"item_options" "blah: 2",
  "item_name" "Awesome Deal",
  "item_price" "9.99"})
nil
Run Code Online (Sandbox Code Playgroud)

我希望这种循序渐进的方法可以帮助您.