我有一张代表数据库的地图。键是字段,值是事务。值的顺序很重要;第一个元素是交易,第二个元素是第二个交易,等等。
{"a" (72144305 72144777 72145239),
"b" (110.5 200.0 50.0),
"c" (10.0 0.0 5.0),
"d" (20.2 30.0 15.0),
"e" ("control" "test" "unmanaged")}
我想把它转换成一个交易列表
[(72144305 110.5 10.0 20.2 "control"), (72144777 200.0 0.0 30.0 "test"), (72145239 50.0 5.0 15.0 "unmanaged")]
换句话说,取每个值的第一个元素并将其放入一个序列中,然后取每个值的第二个元素,将它们放入一个序列中,并对值中的最后一个元素执行相同的操作。
我试过的:
我试过递归解决它
(defn get-transactions
([m]
(let [data (vals m)]
(if (nil? (rest m))
(map first data)
(get-transactions (map rest data))))))
并且我考虑过使用
(map #(nth % 0) (vals data)
来提取单个元素,然后将其映射到 (vals data)
上,但一直无法弄清楚确切的语法。
使用递归解决方案,我无法弄清楚如何传回删除了
(map first data)
的地图。它目前给出了一个错误:
java.lang.Object/toString (Object.java:162) 处的执行错误 (ClassCastException)。 clojure.lang.LazySeq 与 java.util.Map$Entry 不兼容
编辑:我有一个几乎可行的解决方案。输入是
(vals m)
.
(defn get-transactions
"Call on vals m"
([data]
(if (= 1 (count (first data)))
(map first data)
(vec (conj (get-transactions (map rest data)) (map first data))))))
出于某种原因,它没有正确分组。示例输出
[(72144777 200.0 0.0 30.0 "test") 72145239 50.0 5.0 15.0 "unmanaged (72144305 110.5 10.0 20.2 "control")]
问题是中间事务不在一个序列中
Clojure hash-maps 没有默认排序,所以你不应该依赖条目的顺序。
小地图是有序的,因为它们使用数组映射类型而不是哈希映射类型,阈值是 8 个条目(我建议你阅读 Are maps in Clojure ordered?),但是如果你希望你的地图变得更大,你必须注意这一点,并可能使用不同的数据结构。
就是说,如果你想用 seqs 精确地返回向量,你可以这样做:
(def data '{"a" (72144305 72144777 72145239),
"b" (110.5 200.0 50.0),
"c" (10.0 0.0 5.0),
"d" (20.2 30.0 15.0),
"e" ("control" "test" "unmanaged")})
(defn get-transactions [data]
(if (seq (first data))
(conj (get-transactions (map rest data))
(map first data))
[]))
(get-transactions (vals data))
=> [(72145239 50.0 5.0 15.0 "unmanaged") (72144777 200.0 0.0 30.0 "test") (72144305 110.5 10.0 20.2 "control")]
或更短:
(apply mapv list (vals data))
如果你想返回一个向量序列(或sequence of sequences),你也可以这样做:
(defn get-transactions [data]
(when (seq (first data))
(cons (mapv first data)
(get-transactions (mapv rest data)))))
您还可以使用我为处理 CSV 数据而编写的实用程序。
您的数据位于“列矩阵”(又名属性矩阵)中,其中表中的每个“列”都是一个单独的属性
a
,b
等。也就是说,您有一个从属性名称到该属性的值序列。
表示数据的另一种方法是拥有一系列“实体”,每个实体都是从属性名称到该实体的单个值的映射。
要从属性映射转换为实体序列,只需执行以下操作:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[tupelo.csv :as csv]
))
(def data
'{"a" (72144305 72144777 72145239),
"b" (110.5 200.0 50.0),
"c" (10.0 0.0 5.0),
"d" (20.2 30.0 15.0),
"e" ("control" "test" "unmanaged")})
(verify
(is= (csv/attrs->entities data)
[{"a" 72144305, "b" 110.5, "c" 10.0, "d" 20.2, "e" "control"}
{"a" 72144777, "b" 200.0, "c" 0.0, "d" 30.0, "e" "test"}
{"a" 72145239, "b" 50.0, "c" 5.0, "d" 15.0, "e" "unmanaged"}]))
使用我最喜欢的模板项目构建。您可能还对 Clojure 中 更通用的二维数组处理 的相关实体感兴趣。主要版本使用普通的不可变 Clojure 向量。第二个命名空间使用可变 Java 数组来提高速度(仅在需要时!)。