; Write a function that will take your list of maps and convert it back to a CSV
; string. You’ll need to use the clojure.string/join function.
(defn to-json [suspects]
( into {} (clojure.string/join ", " [(get :name suspects)])))
(to-json {:name "Roelof" :glitter-index 2})
; Execution error (IllegalArgumentException) at chapter4/to-json (form-init1060733672431633963.clj:82).
; Don't know how to create ISeq from: java.lang.Character
into will loop over the elements of the string returned by join, but because you said into {} it will try to construct a map out of those elements, but when you into a map the expectations is that you have a sequence of sequence like:
(into {} [[:a "a"] [:b "b"]])
So see how the elements of the first sequence must also be sequences (or something that can be converted to a sequence like the vectors I’m using here, which in Clojure is called a seqable).
But in your case you are calling it like so:
(into {} [\a \b \c]) where the elements of the sequence are Characters, and Characters are not seqable (can’t be converted to a sequence), so when into tries to convert them it fails with the error you see.
I’m not really sure I understand what you’re trying to do to be honest. Can you show the expected result of to-json when called with (to-json {:name "Roelof" :glitter-index 2}) ?
I’ve sent you a solution via PM, but for posterity:
(defn maps->csv [coll-of-maps]
(let [header (-> coll-of-maps first keys) ;; we take the keys to use as a csv header
to-vals (apply juxt header) ;; a function that will extract the map values in the same order as header
lines (map (fn[m] (->> m ;; we take m
to-vals ;; extract the values
(clojure.string/join ", "))) coll-of-maps) ;; and join them as strings
header-string (->> header (map name) (clojure.string/join ", "))] ;; header string
(str header-string "\n" (clojure.string/join "\n" lines)))) ;; the whole thing we want
(maps->csv [{:name "Edward" :glitter-index 10} {:name "Bella" :glitter-index 0}])
;; => "name, glitter-index\nEdward, 10\nBella, 0"
You should probably have a look at juxt over at clojuredocs.org. It’s a very useful function. In general, you can iterate over the values of a map using vals, but since maps are not ordered, and here you need the fields to be in the same order, we can use juxt to generate a function that gives us the values of the map in a vector, in consistent order.
A useful thing to do is to run this stuff in the CIDER debugger (or if you’re not using Emacs, execute each step with some example data) to see what is going on in each transformation. It’s all simple stuff, but it can be a bit confusing when you’re starting out.
Ok, I’m not sure whether Calva supports debugging yet, so what you might want to do is to eval the separate expressions with test data… You could comment out the later bindings in the let form and return the last uncommented one, and repeat this adding the commented forms back one by one.
It’s not uncommon to do that while developing, actually. you start with a let form, and add steps to the transformation until you reach your target. Then you can re-arrange those if needed (e.g, using threading macros, or in some cases you’ll want to use transducers, etc.)
This is a bit cumbersome to show here, but it’s fairly quick to do in the editor:
(defn maps->csv [coll-of-maps]
(let [header (-> coll-of-maps first keys) ;; we take the keys to use as a csv header
; to-vals (apply juxt header) ;; a function that will extract the map values in the same order as header
; lines (map (fn[m] (->> m ;; we take m
; to-vals ;; extract the values
; (clojure.string/join ", "))) coll-of-maps) ;; and join them as strings
; header-string (->> header (map name) (clojure.string/join ", "))
]
#_(str header-string "\n" (clojure.string/join "\n" lines))
header)) ;; the whole thing we want
(maps->csv [{:name "Edward" :glitter-index 10} {:name "Bella" :glitter-index 0}])
;; => (:name :glitter-index)
(defn maps->csv [coll-of-maps]
(let [header (-> coll-of-maps first keys) ;; we take the keys to use as a csv header
to-vals (apply juxt header) ;; a function that will extract the map values in the same order as header
; lines (map (fn[m] (->> m ;; we take m
; to-vals ;; extract the values
; (clojure.string/join ", "))) coll-of-maps) ;; and join them as strings
; header-string (->> header (map name) (clojure.string/join ", "))
]
#_(str header-string "\n" (clojure.string/join "\n" lines))
to-vals)) ;; the whole thing we want
(maps->csv [{:name "Edward" :glitter-index 10} {:name "Bella" :glitter-index 0}])
;; => #function[clojure.core/juxt/fn--5822]
(defn maps->csv [coll-of-maps]
(let [header (-> coll-of-maps first keys) ;; we take the keys to use as a csv header
to-vals (apply juxt header) ;; a function that will extract the map values in the same order as header
lines (map (fn[m] (->> m ;; we take m
to-vals ;; extract the values
(clojure.string/join ", "))) coll-of-maps) ;; and join them as strings
; header-string (->> header (map name) (clojure.string/join ", "))
]
#_(str header-string "\n" (clojure.string/join "\n" lines))
lines)) ;; the whole thing we want
(maps->csv [{:name "Edward" :glitter-index 10} {:name "Bella" :glitter-index 0}])
;; => ("Edward, 10" "Bella, 0")
Exercises
One of the best ways to develop your functional programming skills is to try to implement existing functions. To that end, most of the following exercises suggest a function for you to implement, but don’t stop there; go through the Clojure cheat sheet (http://clojure.org/cheatsheet/) and pick more!
You used (comp :intelligence :attributes) to create a function that returns a character’s intelligence. Create a new function, attr, that you can call like (attr :intelligence) and that does the same thing.