I cannot use +,- apply, map in anyway in the collection.
Technically, strings are seqs of characters, so you can map/filter/reduce them in that context.
It’s not what you want here, but good to know that the seq abstraction can be extended there.
(def x (-> (slurp “nums.csv” ) (clojure.string/split #“\n”)))
I don’t have your data, so I’ll synthesize some:
(def data (->> (range 20)
(partition 4)
(map (fn [xs] (clojure.string/join \, xs)))
(clojure.string/join \newline)))
(spit "blah.csv" data)
So the contents of the file are:
0,1,2,3
4,5,6,7
8,9,10,11
12,13,14,15
16,17,18,19
We can read it in one pass into memory (this is not always the best option, but it’s trivial enough for a vast number of use cases). As you did, I leverage slurp, then dissect the string into rows using clojure.string/split-lines (a built in), then into vectors of comma delimited number strings using clojure.string/split. This yields a sequence of rows, where rows are sequences of character strings. It’s where you stopped before.
The missing piece is to parse row entries into numbers. I am assuming longs here. So I map into a vector (using mapv for convenience) with the function parse-long (in older versions of clojure you may see #(Long/parseLong %) but we have parsing functions provided now).
(defn read-csv [path]
(->> path
slurp
clojure.string/split-lines
(map (fn [row]
(->> (clojure.string/split row #",")
(mapv parse-long))))))
So now we can read our csv of numbers (in this narrow context) and work with the resulting nested collection:
(->> "blah.csv"
read-csv
(apply concat)
(reduce +))
;;190
This is serviceable for small problems (things like advent of code puzzles fit this kind of pattern quite a bit). For actual munging of “real” csv/tsv or other encodings with more complex types, I would recommend a stronger library. charred is very efficient but like data.csv is only concerned with the csv syntax and not inferring values. semantic-csv can handle parsing/coercing values. tech.ml.dataset and its derivatives can handle a lot too (and uses charred under the hood for csv parsing):
user=> (require '[tech.v3.dataset :as ds])
nil
user=> (def nums (ds/->dataset "blah.csv" {:header-row? false}))
#'user/nums
user=> nums
blah.csv [5 4]:
| column-0 | column-1 | column-2 | column-3 |
|---------:|---------:|---------:|---------:|
| 0 | 1 | 2 | 3 |
| 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 |
While there is a dedicated query and transformation API, datasets implement clojure’s persistent collection APIs. At their base, they are mappings of column-names (any object, typically strings or keywords or numbers) to columns (a custom type). This means they conform to the semantics of persistent maps when faced with seq, count, keys, vals, reduce, etc. The column type also has plenty of optimized operations, but it also participates in the apis for our indexed collections (like a vector). So we can treat this column-major format as if it were a map of vectors: like
{"column-0" [0 4 8 12 16],
"column-1" [1 5 9 13 17],
"column-2" [2 6 10 14 18],
"column-3" [3 7 11 15 19]}
user=> (first nums)
["column-0"
#tech.v3.dataset.column<int16>[5]
column-0
[0, 4, 8, 12, 16]]
user=> (->> nums vals (apply concat))
(0 4 8 12 16 1 5 9 13 17 2 6 10 14 18 3 7 11 15 19)
user=> (->> nums vals (apply concat) (reduce +))
190
You can also traverse the values as a seq-of-maps or a seq-of-vectors, if you need record or row-major access.
user=> (ds/rows nums)
[{"column-0" 0, "column-1" 1, "column-2" 2, "column-3" 3}
{"column-0" 4, "column-1" 5, "column-2" 6, "column-3" 7}
{"column-0" 8, "column-1" 9, "column-2" 10, "column-3" 11}
{"column-0" 12, "column-1" 13, "column-2" 14, "column-3" 15}
{"column-0" 16, "column-1" 17, "column-2" 18, "column-3" 19}]
user=> (ds/rowvecs nums)
[[0 1 2 3] [4 5 6 7] [8 9 10 11] [12 13 14 15] [16 17 18 19]]