我们假设我有以下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创建带有关键字的dictonary和每行的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))
答案 0 :(得分:3)
以下是一种解决方案,您可以使用该文件的第一行(标题)来计算关键字:
(defn headers [line]
(map keyword (str/split line #";")))
然后解析一行并返回带标题的地图
(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)))
答案 1 :(得分:2)
你可以这样做吗?
(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))