Dictionary 将带有模式的Clojure映射条目提取到映射列表中?

Dictionary 将带有模式的Clojure映射条目提取到映射列表中?,dictionary,clojure,functional-programming,Dictionary,Clojure,Functional Programming,我有一张这样的地图(1个或多个项目混合在一起): 我想把它变成这样: [{: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"}] 因此,我想通过项目键将它们分开: (def ite

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

我想把它变成这样:

[{: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"}]
因此,我想通过项目键将它们分开:

(def item-keys [:item_name :item_options :item_price :item_quantity])
我猜我可以用
map
walk
但我不知道该怎么办——我对Clojure还很陌生

我先从

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

如果使用正则表达式不是强制性的,并且假设
混合映射
键中的“后缀”是从1到
num items
的数字,则可以直接解决问题

结果中的哈希映射应该与项目的数目相同。我们有
num items
,因此我们可以映射从1到
num items
的范围。在每个步骤中,我们可以为当前项目编号创建一个哈希映射。要创建每个散列映射,我们可以映射
项键
,并将每个项键转换为映射项。映射项中的键是项键本身。该值来自
混合映射
。我们只需要一种方法,从项目编号和项目键为
混合映射创建键。在此过程中,我们不应忘记并非每个项都有每个键,因此我们需要处理nil值。综上所述,我们有以下几点

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

(def item-keys [:item_name :item_options :item_price :item_quantity])

(defn parse-items
   [mixed-map]
   (let [num-items (Integer/parseInt (:itemCount mixed-map))
         all-item-numbers (range 1 (inc num-items))
         mixed-map-key (fn [n k] (keyword (str (name k) "_" n)))
         map-entry (fn [n k] 
                     (when-let [v (mixed-map (mixed-map-key n k))] 
                       [k v]))
         map-entries (fn [n] (map #(map-entry n %) item-keys))]
     (mapv #(into {} (map-entries %)) all-item-numbers)))

(clojure.pprint/pprint (parse-items items))

; => [{:item_name "Great Deal",
; =>   :item_options "foo: 3",
; =>   :item_price "9.99",
; =>   :item_quantity "1"}
; =>  {:item_name "Awesome Deal",
; =>   :item_options "blah: 2",
; =>   :item_price "9.99"}]
; => nil

如果使用正则表达式不是强制性的,并且假设
混合映射
键中的“后缀”是从1到
num items
的数字,则可以直接解决问题

结果中的哈希映射应该与项目的数目相同。我们有
num items
,因此我们可以映射从1到
num items
的范围。在每个步骤中,我们可以为当前项目编号创建一个哈希映射。要创建每个散列映射,我们可以映射
项键
,并将每个项键转换为映射项。映射项中的键是项键本身。该值来自
混合映射
。我们只需要一种方法,从项目编号和项目键为
混合映射创建键。在此过程中,我们不应忘记并非每个项都有每个键,因此我们需要处理nil值。综上所述,我们有以下几点

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

(def item-keys [:item_name :item_options :item_price :item_quantity])

(defn parse-items
   [mixed-map]
   (let [num-items (Integer/parseInt (:itemCount mixed-map))
         all-item-numbers (range 1 (inc num-items))
         mixed-map-key (fn [n k] (keyword (str (name k) "_" n)))
         map-entry (fn [n k] 
                     (when-let [v (mixed-map (mixed-map-key n k))] 
                       [k v]))
         map-entries (fn [n] (map #(map-entry n %) item-keys))]
     (mapv #(into {} (map-entries %)) all-item-numbers)))

(clojure.pprint/pprint (parse-items items))

; => [{:item_name "Great Deal",
; =>   :item_options "foo: 3",
; =>   :item_price "9.99",
; =>   :item_quantity "1"}
; =>  {:item_name "Awesome Deal",
; =>   :item_options "blah: 2",
; =>   :item_price "9.99"}]
; => nil

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

  • 按关键字后缀对给定映射中的键值对进行分组
  • 根据分组创建贴图,并将其放入新向量中
  • 如果这些假设是正确的,下面是我的解决方案

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

    然后,使用项目键筛选项目

    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
    
    其次,使用
    groupby
    函数按后缀对修改的键值对进行分组

    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
    
    (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 {}))))))
    
    并将分组转换为地图

    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
    
    最后,将这些步骤集成到函数中

    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
    
    (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 {}))))))
    
    它起作用了

    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
    

    我希望这种循序渐进的方法对您有所帮助。

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

  • 按关键字后缀对给定映射中的键值对进行分组
  • 根据分组创建贴图,并将其放入新向量中
  • 如果这些假设是正确的,下面是我的解决方案

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

    然后,使用项目键筛选项目

    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
    
    其次,使用
    groupby
    函数按后缀对修改的键值对进行分组

    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
    
    (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 {}))))))
    
    并将分组转换为地图

    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
    
    最后,将这些步骤集成到函数中

    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
    
    (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 {}))))))
    
    它起作用了

    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
    

    我希望这种循序渐进的方法能帮助您。

    一个完整而简单的解决方案是:

    (->> item-map
         (keep (fn [[k v]]
                 (let [[_ name id] (re-find #"(.+)_(\d+)$" (name k))]
                   (if id
                     [[(dec (Integer/parseInt id)) (keyword name)] v]))))
         (sort-by ffirst)
         (reduce (partial apply assoc-in) []))
    
    如果希望允许非连续ID,或者事先不知道它们是否为0索引,则可以修改如下算法:

    (->> item-map
         (keep (fn [[k v]]
                 (let [[_ name id] (re-find #"(.+)_(\d+)$" (name k))]
                   (if id
                     [id [(keyword name) v]]))) )
         (sort-by first)
         (partition-by first)
         (map #(->> %
                    (map second)
                    (into {}))))
    
    注意,此算法允许的松散输入要求取消了无损转换的保证(假设除
    :项计数
    外,不需要任何未编号的键)。例如,不能指望向后转换算法再次从结果中产生与
    项映射
    相等的值

    为了清晰起见,我省略了对
    项键的过滤,因为这是一个单独的问题。您可以将
    项键
    定义为散列集,并将lambda更改为
    保留
    ,将其集成到两种算法中,如下所示:

    (let [[_ name id] (re-find #"(.+)_(\d+)$" (name k))
          k (-> name keyword item-keys)]
      (if (and id k)
        ;; ...
    

    一个完整而简单的解决方案是:

    (->> item-map
         (keep (fn [[k v]]
                 (let [[_ name id] (re-find #"(.+)_(\d+)$" (name k))]
                   (if id
                     [[(dec (Integer/parseInt id)) (keyword name)] v]))))
         (sort-by ffirst)
         (reduce (partial apply assoc-in) []))
    
    如果希望允许非连续ID,或者事先不知道它们是否为0索引,则可以修改如下算法:

    (->> item-map
         (keep (fn [[k v]]
                 (let [[_ name id] (re-find #"(.+)_(\d+)$" (name k))]
                   (if id
                     [id [(keyword name) v]]))) )
         (sort-by first)
         (partition-by first)
         (map #(->> %
                    (map second)
                    (into {}))))
    
    注意,此算法允许的松散输入要求取消了无损转换的保证(假设除
    :项计数
    外,不需要任何未编号的键)。例如,不能指望向后转换算法再次从结果中产生与
    项映射
    相等的值

    为了清晰起见,我省略了对
    项键的过滤,因为这是一个单独的问题。您可以将
    项键
    定义为散列集,并将lambda更改为
    保留
    ,将其集成到两种算法中,如下所示:

    (let [[_ name id] (re-find #"(.+)_(\d+)$" (name k))
          k (-> name keyword item-keys)]
      (if (and id k)
        ;; ...
    
    (与其他发布的答案一样,它忽略了您指定的从“foo:3”和“blah:2”到“foo”和“blah”的转换,我认为这应该单独处理。)


    (与其他发布的答案一样,它忽略了您指定的从“foo:3”和“blah:2”到“foo”和“blah”的转换,我认为这应该单独处理。)

    我担心您的数据

    • 我们不需要
      :itemCount
    • 数字应该是数字,而不是字符串
    • 如果,顾名思义,可能有几个
      :item\u options\u…
      对于每个项目,值应该是映射,而不是字符串
    因此,您的数据应该如下所示:<