从地图中,我如何按顺序对序列的元素(值)进行分组?

问题描述 投票:0回答:2

我有一张代表数据库的地图。键是字段,值是事务。值的顺序很重要;第一个元素是交易,第二个元素是第二个交易,等等。

{"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 etl
2个回答
1
投票

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)))))

0
投票

您还可以使用我为处理 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 数组来提高速度(仅在需要时!)。

© www.soinside.com 2019 - 2024. All rights reserved.