Map to json

Helllo,

And again stuck on this :

; 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

get takes the map first:

(get suspects :name)

changed it but the same error :

(defn to-json [suspects]
  (  into {} (clojure.string/join ", " [(get suspects :name)])))

Well it also doesn’t make sense I think to call into on a string.

Try just this: (into {} "foo, bar") and what happens?

Oh actually I know what happens.

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.

Yep, the same error

So i have to find a way to put what I have in a hash


(defn to-json [suspects]
  (?? (clojure.string/join ", " [(get suspects :name) (get suspects :glitter-index)])))

or find a way to call the both gets multiple time so it work with more then 1 entry in a map

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}) ?

im at the totally wrong way. IM was going for json where I have to go for csv

So for example { {:name "John Doe" :glitter-index 2] {:name "Roelof" :glitter-index 3}}

must give

John Doe, 2
Roelof, 3

so time to sleep and begin from stratch

1 Like

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.

For reference, the problem is at the end of this page: https://www.braveclojure.com/core-functions-in-depth/

Thanks, I use VS code with Calva so I do not know if and how I can use a debugger

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")

Thanks a lot, I have figured this out with the debugger from Calva.
Here a lot is used then what I learned at this page https://www.braveclojure.com/core-functions-in-depth/

but is oke

1 Like

Why are the challenges in a book always so vague

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.