如何将 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)))
假设我有以下 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)))