Jeroen
Jeroen

Reputation: 63729

How to convert a lazy sequence to a map?

I've got a lazy sequence that, when sent to println, shows like this:

(((red dog) (purple cat)) ((green mouse) (yellow bird)))

Note that this is the result of reading a csv and trimming all "cell" values, hence (a) the fact that it's lazy at the time I want to print it and (b) in the future innermost lists might be more than 2 strings because more columns are added.

I'm trying to juse clojure.pprint/print-table to print this in a two-column table. I'm having a pretty hard time because print-table seems to want a map with data.

Here's a repro:

;; Mock the lazy data retrieved from a csv file
(defn get-lazy-data []
  (lazy-seq '('('("red" "dog") '("purple" "cat")) '('("green" "mouse") '("yellow" "bird")))))

(defn -main []
  (let [data       (get-lazy-data)]
      (println "Starting...")
      (println data)
      (println "Continuing...")
      (print-table data)
      (println "Finished!")))

This gives an error:

Exception in thread "main" java.lang.ClassCastException: clojure.lang.Symbol cannot be cast to java.util.Map$Entry

I've tried various options:

Basically I know I have an XY-problem but now I want answers to both questions:

Upvotes: 1

Views: 1018

Answers (2)

Alan Thompson
Alan Thompson

Reputation: 29958

A related point to your problem is how you print out your data. Clojure has two ways to print:

(dotest
  (println ["hello" "there" "everybody"])      ; #1
  (prn     ["hello" "there" "everybody"]))     ; #2

#1  => [hello there everybody]
#2  => ["hello" "there" "everybody"]

For strings the presence of quotes in #2 makes a huge difference in understanding what is happening. The prn function produces output that is machine-readable (like what you type in your source code). You really need that if you have strings involved in your data.

Look at the difference with symbols:

  (println ['hello 'there 'everybody])
  (prn     ['hello 'there 'everybody])

  ; doesn't matter if you quote the whole form or individual symbols    
  (println '[hello there everybody])
  (prn     '[hello there everybody])

all results are the same:

[hello there everybody]
[hello there everybody]
[hello there everybody]
[hello there everybody]

The point is that you need prn to tell the difference between symbols and strings when you print results. Note that the prn output format (with double-quotes) happens automatically if you use pprint:

(def data
  [["here" "we" "have" "a" "lot" "of" "strings" "in" "vectors"]
   ["here" "we" "have" "a" "lot" "of" "strings" "in" "vectors"]
   ["here" "we" "have" "a" "lot" "of" "strings" "in" "vectors"]])

(clojure.pprint/pprint data) =>
  [["here" "we" "have" "a" "lot" "of" "strings" "in" "vectors"]
   ["here" "we" "have" "a" "lot" "of" "strings" "in" "vectors"]
   ["here" "we" "have" "a" "lot" "of" "strings" "in" "vectors"]]

Upvotes: 0

Taylor Wood
Taylor Wood

Reputation: 16194

How do I pretty print a lazy sequence of pairs of pairs of strings as a table on the console?

The fact that your "rows" seem to be grouped in pairs is odd, assuming you want a two-column table of color/animal, so we can remove the extra grouping with mapcat identity then zipmap those pairs with the desired map keywords:

(def my-list
  '(((red dog) (purple cat)) ((green mouse) (yellow bird))))
(def de-tupled (mapcat identity my-list))
(map #(zipmap [:color :animal] %) de-tupled)
=> ({:color red, :animal dog} {:color purple, :animal cat} {:color green, :animal mouse} {:color yellow, :animal bird})

(clojure.pprint/print-table *1)
| :color | :animal |
|--------+---------|
|    red |     dog |
| purple |     cat |
|  green |   mouse |
| yellow |    bird |

It's not clear from the question, but it seems like you want to support an arbitrary number of "columns" which kinda precludes having fixed names for them. In that case you can do something like this:

(def my-list ;; added third mood "column"
  '(((red dog happy) (purple cat sad)) ((green mouse happy) (yellow bird sad))))
(def de-tupled (apply concat my-list))
(clojure.pprint/print-table (map #(zipmap (range) %) de-tupled))
|      0 |     1 |     2 |
|--------+-------+-------|
|    red |   dog | happy |
| purple |   cat |   sad |
|  green | mouse | happy |
| yellow |  bird |   sad |

How can I convert a lazy sequence to a map (where e.g. keys are the indexes)?

(def my-list
  '(((red dog) (purple cat)) ((green mouse) (yellow bird))))
(zipmap (range) my-list)
=> {0 ((red dog) (purple cat)), 1 ((green mouse) (yellow bird))}

Upvotes: 3

Related Questions