Reputation: 21
(defn loadData [filename]
(with-open [rdr (io/reader filename)]
(doseq [line (line-seq rdr)
:let [ [id & data]
(clojure.string/split line #"\|")] ]
[
(Integer/parseInt id)
data
])
))
My file's content:
1|Xxx|info1|222-2222
2|Yyy|info2|333-3333
3|Zzz|info3|444-4444
How do I get this function to return a list like :
{
{1, Xxx, info1, 222-2222}
{2, Yyy, info2, 333-3333}
{3, Zzz, info3, 444-4444}
}
I also want to know how to get my function loadData to return this list above instead of NIL.
Please help! Thanks in advance...
Upvotes: 0
Views: 58
Reputation: 36
(def my-file-content
(.getBytes "1|Xxx|info1|222-2222\n2|Yyy|info2|333-3333\n3|Zzz|info3|444-4444"))
;; See https://guide.clojure.style/ for common naming style.
(defn load-data
[filename]
;; Well done, `with-open` is correctly used here!
(with-open [rdr (io/reader filename)]
;; See https://clojuredocs.org/clojure.core/doseq for examples of `for` and `doseq`. After reading examples and documentation, it will probably become clear why `for` is what you want, and not `doseq`.
(for [line (line-seq rdr)
:let [[id & data] (clojure.string/split line #"\|")]]
[(Integer/parseInt id)
data])))
;; But `(load-data my-file-content)` raises an exception because `for` returns a lazy sequence. Nothing happens before you actually display the result, so the course of actions is:
;; - Open a reader;
;; - Define computations in a `LazySeq` as result of `for`;
;; - Close the reader;
;; - The function returns the lazy seq;
;; - Your REPL wants to display it, so it tries to evaluate the first elements;
;; - Now it actually tries to read from the reader, but it is closed.
;; This lazyness explains why this returns no exception (you don't look at what's inside the sequence):
(type (load-data my-file-content))
;; => clojure.lang.LazySeq
;; The shortest fix is:
(defn load-data
[filename]
(with-open [rdr (io/reader filename)]
;; https://clojuredocs.org/clojure.core/doall
(doall
(for [line (line-seq rdr)
:let [[id & data] (clojure.string/split line #"\|")]]
[(Integer/parseInt id)
data]))))
(load-data my-file-content)
;; => ([1 ("Xxx" "info1" "222-2222")] [2 ("Yyy" "info2" "333-3333")] [3 ("Zzz" "info3" "444-4444")])
;; This is not what you want. Here is the shortest fix to coerce this (realised) lazy sequence into what you want:
(defn load-data
[filename]
(with-open [rdr (io/reader filename)]
;; https://clojuredocs.org/clojure.core/doall
(doall
(for [line (line-seq rdr)
:let [[id & data] (clojure.string/split line #"\|")]]
(cons (Integer/parseInt id) data)))))
(load-data my-file-content)
;; => '((1 "Xxx" "info1" "222-2222") (2 "Yyy" "info2" "333-3333") (3 "Zzz" "info3" "444-4444"))
;; Also, note that a list is represented as `'(1 2 3)` or `[1 2 3]` for a vector. `{1 2 3}` is a syntax error, curly brackets are for maps.
(defn load-data
[filename]
(with-open [rdr (io/reader filename)]
(->> (line-seq rdr)
(mapv #(clojure.string/split % #"\|")))))
(load-data my-file-content)
;; => [["1" "Xxx" "info1" "222-2222"] ["2" "Yyy" "info2" "333-3333"] ["3" "Zzz" "info3" "444-4444"]]
;; Note that here we no longer have a list of lists, but a vector of vectors. In Clojure lists are lazy and vectors are not, so this is why `mapv`, which returns a vector, works fine with an open reader.
Upvotes: 1