Dictionary 将带有模式的Clojure映射条目提取到映射列表中?
我有一张这样的地图(1个或多个项目混合在一起): 我想把它变成这样: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
[{: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…
对于每个项目,值应该是映射,而不是字符串
因此,您的数据应该如下所示:<