将列表列表转换为Clojure中列表列表的映射

时间:2010-10-15 17:02:36

标签: list clojure maps

我有一个列表列表,并希望将其放入一个映射中,其中键是列表中的常用值之一(本例中为动物名称)。我知道如何使用{}和从列表创建一个地图,但这不是我想要的。我希望地图中的键(动物名称)能够引用这些值列表。

我创建了一个列表列表

(def animals (list '("tiger" "fur" "yellow & black stripes") '("tiger" "weight" "150") '("tiger" "home" "India") '("elephant" "skin" "gray") '("elephant" "weight" "1500") '("elephant" "home" "Africa") '("frog" "skin" "green") '("frog" "diet" "insects")))

动物

(("tiger" "fur" "yellow & black stripes") ("tiger" "weight" "150") ("tiger" "home" "India") ("elephant" "skin" "gray") ("elephant" "weight" "1500") ("elephant" "home" "Africa") ("frog" "skin" "green") ("frog" "diet" "insects"))

这并不完全符合我的要求(但差不多)

(def animal-map (into {} (for [[name attribute value] animals] [name (list attribute value)])))

当我想为每个键添加多个列表时,每个键只会生成一个列表

动物地图(列表地图)

{"tiger" ("home" "India"), "elephant" ("home" "Africa"), "frog" ("diet" "insects")}

这是我希望最终的结果。

动物图(列表清单地图)

{"tiger" ("fur" "yellow & black stripes") ("weight" "150") ("home" "India"),   
"elephant" ("skin" "gray") ("weight" "1500") ("home" "Africa"),   
"frog" ("skin" "green") ("diet" "insects")}  

3 个答案:

答案 0 :(得分:8)

关于您期望的结果,您发布的最终地图并没有按照您的想法进行。在这张地图中:

{"tiger" ("fur" "yellow & black stripes") ("weight" "150") ("home" "India")}

这不是包含一个键和三个值的地图,这是一个包含两个键("tiger"("weight" "150"))和两个值的地图。

以下内容并未直接回答您的问题,而是关于惯用Clojure的一句话。如果您为手头的工作选择合适的数据结构,您将在Clojure中度过更轻松的时光。

对于您的初始数据,您应该使用向量而不是列表,除非您有充分的理由使用列表。矢量对于许多操作(例如随机访问)表现更好;你不需要像你做一个列表那样引用一个向量;和向量表示你正在处理数据而不是代码。

如果您的数据必须从前面而不是后面增长,或者如果您将源代码生成为数据(例如,在宏中),则列表是合适的。

[["tiger" "fur" "yellow & black stripes"]
 ["tiger" "weight" "150"]
 ["tiger" "home" "India"]
 ...]

对于最终数据,请考虑使用字符串映射来映射而不是将字符串映射到列表。使用assoc-in创建地图地图很容易,因为它会为您创建中间地图。然后,您可以使用get-in访问数据的子键。

要在字符串映射中访问动物的属性到列表,您必须对属性列表进行线性搜索。这对于编码和执行效果来说都很尴尬。

user> (def animal-map (reduce (fn [m [animal k v]]
                                (assoc-in m [animal k] v))
                              {} animals))
#'user/animal-map
user> animal-map
{"frog" {"diet" "insects", "skin" "green"}, 
 "elephant" {"home" "Africa", "weight" "1500", "skin" "gray"}, 
 "tiger" {"home" "India", "weight" "150", "fur" "yellow & black stripes"}}

user> (get-in animal-map ["frog" "diet"])
"insects"

最后,我不确定这些数据的来源,但考虑使用关键字而不是字符串来处理这些键。它可能适合或不适合动物的名字,但它几乎肯定适合动物的属性(体重,家庭,饮食)。

如果您的密钥是关键字,则甚至不需要get-in,您可以直接将关键字用作功能,或使用->。你不能用字符串来做这件事。

user> (def animal-keyword-map (reduce (fn [m [animal k v]]
                                        (assoc-in m [(keyword animal) (keyword k)] v))
                                      {} animals))
#'user/animal-keyword-map
user> animal-keyword-map
{:frog {:diet "insects", :skin "green"}, 
 :elephant {:home "Africa", :weight "1500", :skin "gray"}, 
 :tiger {:home "India", :weight "150", :fur "yellow & black stripes"}}
user> (:weight (:tiger animal-keyword-map))
"150"
user> (-> animal-keyword-map :tiger :weight)
"150"

答案 1 :(得分:0)

将您的列表合并到{}中,替换重复的密钥。

(reduce (fn [m [k & vals]] (assoc m k (conj (m k) vals))) {} animals)

答案 2 :(得分:0)

为了避免以后的条目替换以前的条目,您需要创建一堆映射,然后以受控方式将它们合并在一起。万岁!我们只有merge-with来完成这份工作!

首先我们将列表转换为一系列地图,方法是将每个列表中的第一个字符串作为键,其余部分作为值。

(map #(hash-map (first %) (rest %)) animals)

({"tiger" ("fur" "yellow & black stripes")} 
 {"tiger" ("weight" "150")} 
 {"tiger" ("home" "India")} 
 {"elephant" ("skin" "gray")} 
 {"elephant" ("weight" "1500")} 
 {"elephant" ("home" "Africa")} 
 {"frog" ("skin" "green")} 
 {"frog" ("diet" "insects")})

然后让我们尝试将它们与 list合并,因为它会浮现在脑海中。

(apply merge-with list (map #(hash-map (first %) (rest %)) animals))

{"frog" (("skin" "green") ("diet" "insects")), "elephant" ((("skin" "gray") 
("weight"    "1500")) ("home" "Africa")), "tiger" ((("fur" "yellow & black stripes") 
("weight" "150")) ("home" "India"))}

虽然嵌套太深,但看起来更像它。不要担心,我们可以通过将列表创建移动到内部地图来阻止嵌套:

(apply merge-with concat (map #(hash-map (first %) (list (rest %))) animals))

{"frog" (("skin" "green") ("diet" "insects")), "elephant" (("skin" "gray") ("weight" 
"1500") ("home" "Africa")), "tiger" (("fur" "yellow & black stripes") ("weight" "150")
 ("home" "India"))}

我无法准确匹配所需的输出,因为地图只能有一个键值。要“老虎”需要映射到一对列表。而不是一对。