Clojure 如何将地图转换为矢量?

Clojure 如何将地图转换为矢量?,clojure,clojurescript,Clojure,Clojurescript,如果我有一张像这样的地图: {:a 1 :b 2 :c 3} 如何将其转换为向量,如: [:a 1 :b 2 :c 3] 这里有一个用于此目的的简单函数 (ns demo.core (:require [schema.core :as s] [tupelo.schema :as tsk])) (s/defn keyvals :- [s/Any] "For any map m, returns the (alternating) keys & va

如果我有一张像这样的地图:

{:a 1 :b 2 :c 3}
如何将其转换为向量,如:

[:a 1 :b 2 :c 3]

这里有一个用于此目的的简单函数

(ns demo.core
  (:require 
    [schema.core :as s]
    [tupelo.schema :as tsk]))

(s/defn keyvals :- [s/Any]
  "For any map m, returns the (alternating) keys & values of m as a vector, suitable for reconstructing m via
   (apply hash-map (keyvals m)). (keyvals {:a 1 :b 2} => [:a 1 :b 2] "
  [m :- tsk/Map]
  (reduce into [] (seq m)))
使用单元测试:

(ns tst.demo.core
  (:use tupelo.core tupelo.test))

(dotest
  (let [m1 {:a 1 :b 2 :c 3}
        m2 {:a 1 :b 2 :c [3 4]}]
    (is= [:a 1 :b 2 :c 3]   (t/keyvals m1))
    (is= m1 (apply hash-map (t/keyvals m1)))
    (is= m2 (apply hash-map (t/keyvals m2)))))
正如单元测试所示,keyvals是apply hashmap。。。并可用于解构地图。当调用需要关键字args的函数时,它可能很有用。

您可以使用reduce-kv:

我建议使用flatte和vec

在repl上,它将如下所示:

user=> (vec (flatten (vec {:a 1 :b 2 :c 3})))
[:a 1 :b 2 :c 3]
与散射传感器结合使用非常简洁:

(into [] cat {:a 1 :b 2 :c 3})
;;=> [:a 1 :b 2 :c 3]
使用mapcat和vec来实现:

(vec (mapcat identity {:a 1 :b 2 :c 3}))
;; => [:a 1 :b 2 :c 3]

扁平化永远不是正确的答案。在像{:x[12],:y[34]}这样的输入映射上试试这个,你会得到一个可怕的惊喜。我不同意。在{:x[12],:y[34]}的情况下,我们必须检查OP想要什么。也许他们想要[:x12:y34],在这种情况下,这个解决方案对他们有效。或者他们从来没有收藏的巢穴。艾伦问这个问题的明确目的是根据最近一个封闭的问题提供他自己的答案,这个问题有一个类似的前提,但问得很糟糕。他提供了一个可以按照他坚持使用tupelo库的方式工作的函数,标识为=m apply hash-map-keyvals m,并且您的函数失败了一个测试用例。但在大计划中,OP想要什么并不重要,重要的是他们实际要求什么;Stack Overflow的工作是通过搜索引擎轻松找到高质量的答案,因此我们希望找到一个能吸引通过谷歌到达这里的人的答案。我相信我提供的答案对一些人很有用,从谷歌来到这里的人可能需要这个答案。这里有一个通用的用例:考虑使用{:{::x:M}:b{:y:n}:c:}}中的一棵树,程序可能需要树中所有节点的向量,因为任何数量的原因。在这种情况下,结果[:a:x:m:b:y:n:c:z]将是正确的结果。堆栈溢出注释并不是进行扩展讨论的最佳位置。但flatte通常不是一个性能非常好的函数:当您想到没有嵌套集合的简单数据时,它有时看起来不错,但当扩展到嵌套集合时,它会崩溃。在我5年的专业Clojure生涯中,我从来没有理由使用flatten。另外,我可以告诉你,你没有运行你给出的例子,因为它实际上没有提供你所说的预期结果。再举一个例子,说明flatten不是一个向新手推荐的好功能。最好使用identity而不是identity。@gits这是因为我来自common lisp。但在clojure中,在函数名前面使用“表示它是一个函数”不是也不对吗?我知道这不是必须的。但这并没有错——我也经常看到人们使用这种方法。。。或者我的看法是错误的?“identity返回变量clojure.core/identity,而不是包含的函数。Var通过假设包含的值是一个函数并调用它来实现IFn,这就是您的示例工作的原因,但更常见的是使用identity,因此在将Var传递给mapcat之前,会先解除对它的引用。@Lee感谢您的解释。那么我将修改我的答案。为什么在ClojureScript中会出现相反的情况?如果真的有必要,它需要解释…@glts这是一个很好的观点!这个编辑是Marcellinus建议的,但我不知道为什么需要它。我将再次删除它,直到它可以被解释。为了保持返回的顺序,它在ClojureScript中使用into[]进行排序。。。工作原理略有不同。@Marcellinus Maps是一个无序的数据结构,因此顺序在任何情况下都不应该相关。我认为最好假设顺序并不重要,[:b 2:a 1:c 3]也是一个完全有效的结果,即使问题中没有说明,因为正如@glts指出的,虽然Clojure中也有排序映射,但通常映射在Clojure中不排序。如果你想要一个排序的输出,我相信使用sort比reverse更可靠,因为它不会对我们展平的地图类型做出任何假设,[:c3:a1:b2]也会是一个以不同顺序访问键的有效结果吗?是的,因为映射不保证映射条目项的顺序,即键值对。关键是应用哈希映射t/keyvals m1始终是幂等的。
(into [] cat {:a 1 :b 2 :c 3})
;;=> [:a 1 :b 2 :c 3]
(vec (mapcat identity {:a 1 :b 2 :c 3}))
;; => [:a 1 :b 2 :c 3]