List 在Clojure中将列表列表转换为列表映射

List 在Clojure中将列表列表转换为列表映射,list,clojure,maps,List,Clojure,Maps,我有一个列表列表,希望将其放入一个地图中,其中键是列表中的一个公共值(本例中为动物名称)。我知道如何使用into{}和for从列表创建映射,但这并不是我想要的。我希望地图中的键(动物的名字)引用这些值的列表 我已经创建了一个列表列表 (def animals (list '("tiger" "fur" "yellow & black stripes") '("tiger" "weight" "150") '("tiger" "home" "India") '("elephant" "sk

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

我已经创建了一个列表列表

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

(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"))
{"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")}  
这并不完全符合我的要求(但几乎可以)

当我希望每个键都有多个列表时,这将导致每个键只有一个列表

动物地图(列表地图)

这就是我想要的结局。

动物地图(列表地图)


通过将列表合并到{}中,可以替换重复的键

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

为了避免后面的条目替换前面的条目,您需要创建一组贴图,然后以可控的方式将它们合并在一起。好极了我们有合并,仅用于此项工作

首先,我们使用每个列表中的第一个字符串作为键,其余字符串作为值,将列表转换为一系列映射

(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")})
然后,让我们尝试将它们与
列表
合并,因为这是首先想到的事情

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

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

关于你想要的结果,你发布的最终地图没有达到你认为的效果。在此地图中:

{"tiger" ("fur" "yellow & black stripes") ("weight" "150") ("home" "India")}
这不是一个包含一个键和三个值的映射,这是一个包含两个键(
“tiger”
(“weight”“150”)
)和两个值的映射

下面不是直接回答你的问题,而是一个关于习惯用语Clojure的单词。如果您为手头的工作选择合适的数据结构,那么在Clojure中会更轻松

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

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

对于最后的数据,考虑使用字符串映射来映射而不是映射到列表的字符串。使用中的

assoc可以轻松创建地图的地图,因为它可以为您创建中间地图。然后,您可以使用
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"
最后,我不确定这些数据来自何处,但是考虑使用关键字代替字符串。它可能适用于动物的名称,也可能不适用于动物的属性(体重、家庭、饮食)

如果您的关键字是关键字,您甚至不需要
进入
,您可以直接使用关键字作为函数,或者使用
->
。你不能用字符串来做这件事

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"

这是一个很好的尝试,但结果并不完全符合我想要的结果。我希望所有列表都位于同一级别,而不是子列表。我需要弄清楚合并函数使用什么。concat也会生成相同的输出。修复:)并添加了相应的过程。我想您已经得到了。这看起来很有效。非常感谢。现在,我将尝试理解它为什么有效。