如何将 csv 行映射到关键字词典的集合?

How to map a csv line to a collection of keyword dictionaries?

假设我有以下 csv:

DATE;DESC;IN;OUT
11/04/13;Buy new shoes;;90
16/04/13;Wage;5000;
17/04/13;Donate money;;200
;;;
30/04/13;Buy new shoes again;;80

我基本上想解析这个 csv 文件,过滤掉空行,然后再对其执行一些计算。我不想使用任何预制的 csv 库,因为我对 clojure 比较陌生,想通过艰苦的方式学习它。

这是我目前所做的:

(ns calc
  (:require [clojure.java.io :as io]
            [clojure.string :as str]))

(defn filter-empty-lines [coll]
  (filter #(not (.startsWith % ";;;")) coll))

(defn parse-lines [coll]
  (let [columns [:date :desc :out :in]]
     headers (map name columns)
     --> STUCK

(defn calculate-costs [f]
  (->> (io/reader f)
    line-seq
    filter-empty-lines    
    parse-lines))

(calculate-costs "/var/tmp/in_out.csv")

基本上,空行的过滤已经有效,但我有点坚持解析的 csv 行的映射。

我的想法是简单地将行拆分为 ;并使用 zipmap 创建一个包含关键字 和每行的 csv 值的字典,并将其添加到集合中。

我没有让解析行功能正常工作,如果有人能帮忙,我会很高兴。我也很感激任何与 clojure 相关的提示/改进想法。

提前致谢

更新


感谢 bsvingen 的回答,我得到了以下结果:

(defn parse-lines [coll]
  (map #(let [[date desc out in] (clojure.string/split % #";")]
    {:date date :desc desc :out out :in in}) coll))

你可以做这样的事情吗?

(let [[date desc out in] (clojure.string/split "17/04/13;Donate money;;200" #";")]
  {:date date :desc desc :out out :in in})

(了解 destructuring。)

完整的函数如下所示:

(defn make-map [csv-line]
  (let [[date desc out in] (clojure.string/split csv-line #";")]
    {:date date :desc desc :out out :in in}))

(defn parse-lines [coll]
  (map make-map coll))

以下是一个解决方案,您可以使用文件的第一行 (headers) 来计算关键字:

(defn headers [line]
  (map keyword (str/split line #";")))

然后用 headers

解析一行和 return 一张地图
(defn parse-line [headers line]
  (zipmap headers (str/split line #";")))

解析所有文件给出:

(defn parse-lines [coll]
  (let [head (headers (first coll))]
    (map (partial parse-line head) (rest coll))))

现在您有了类似于地图的电子表格,您可以对给定的列求和:

(defn calculate [sheet column-key]
  (->> sheet
       (map column-key)
       (filter (complement nil?))
       (map #(Integer/parseInt %))
       (reduce +)))

计算成本:

(with-open [file (io/reader "./calc.csv")]
  (let [sheet (->> file
                   line-seq
                   filter-empty-lines
                   parse-lines)]
    (calculate sheet :OUT)))