我有一个列表列表,并希望将其放入一个映射中,其中键是列表中的常用值之一(本例中为动物名称)。我知道如何使用{}和从列表创建一个地图,但这不是我想要的。我希望地图中的键(动物名称)能够引用这些值列表。
我创建了一个列表列表
(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")}
答案 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"))}
我无法准确匹配所需的输出,因为地图只能有一个键值。要“老虎”需要映射到一对列表。而不是一对。