0
votes

What I have:

  • Collection (map in this instance, Seqable more generally) of items I want to display in a Markdown table (whichever flavour of Markdown reddit uses).
  • Sequence of accessor functions that produce the contents of each column of the desired table when mapped over the collection.
  • Sequence of those column mappings: (for [x accessors] (map x coll))

What I'm trying to do:

  • Append (repeat "\n") to the sequence of mappings, as the item separator.
  • apply interleave over the sequence-of-sequences.
  • Consume the resulting sequence with clojure.string/join to insert the 'table cell separator' "|" and glue it all together.

I just can't seem to get the first step working. All my attempts seem to append the infinite sequence of \n itself rather than that sequence as a single object in a seq of seqs or similar issues. A little help?

Edit: A little input/output example does make sense for something like this so I'd better add it. For simplicity we'll just list numbers and functions of them. Input:

(markdown-table [[[identity] "Number"]
                 [[(partial * 2)] "Doubled"]] (range 6))

(The strings and such are for making column names - might change that setup later but you can see the accessor functions in there. Just listing the number itself and its doubling.)
For this I have the sequence ((0 1 2 3 4 5) (0 2 4 6 8 10)) and want to end up with the sequence

(0 0 "\n" 1 2 "\n" 2 4 "\n" 3 6 "\n" 4 8 "\n" 5 10 "\n")
3
show input and desired output at least, code if you have it - edbond

3 Answers

2
votes

Clojure already has something similar to what you are trying to do

(defn markdown-table 
  [specs xs] 
  (clojure.pprint/print-table 
    (for [x xs] 
      (into {} 
        (for [{:keys [label fn]} specs] [label (fn x)])))))

(markdown-table [{:label "Number", :fn identity} 
                 {:label "Doubled", :fn (partial * 2)}] 
                (range 6))

Output (could wrap in with-out-str):

| Number | Doubled |
|--------+---------|
|      0 |       0 |
|      1 |       2 |
|      2 |       4 |
|      3 |       6 |
|      4 |       8 |
|      5 |      10 |
0
votes

You're looking for interpose

(def items [1 2 3 4 5])

(def accesors [(fn [x] (inc x))
               (fn [x] (- 10 x))])

(def mappings (for [x accesors]
                (map x items)))
=> ((2 3 4 5 6) (9 8 7 6 5))


(interpose "\n" mappings)
=> ((2 3 4 5 6) "\n" (9 8 7 6 5))

Edit after your sample:

 (map (fn [& args] 
         (apply (juxt identity (partial * 2)) args)) 
      (range 6))
 => ([0 0] [1 2] [2 4] [3 6] [4 8] [5 10])

Then just use interpose on it.

(def accessors [(fn [x] (identity x))
                (fn [x] (* x 2))])

(def mappings (map (fn [& args] 
                     (apply (apply juxt accessors) args)) 
                   (range 6)))

(interpose "\n" mappings)
=> ([0 0] "\n" [1 2] "\n" [2 4] "\n" [3 6] "\n" [4 8] "\n" [5 10])
0
votes

While responding I appear to have found a way that works using my original approach. By placing the mapping-sequences in a vector, I can append the \n sequence as one value to interleave rather than as infinitely many values as with concat, cons and so on. The resulting code was

(defn- markdown-table
  "Create Markdown for a table displaying a collection
  Columns defines the columns to show - give pairs of accessor sequence and display names."
  [columns coll]
  (let[columns-def (str "|" (clojure.string/join "|" (concat (map second columns)
                                                            "\n"
                                                            ;;All columns are center aligned, for now.
                                                            (map (constantly ":--:") columns)))
                       "\n")
       accessors (for [[x _] columns] (apply comp (reverse x))) ;;Reverse so composition is leftmost-first
       columns (for [x accessors] (map x coll))
       item-separated (conj (vec columns) (repeat "\n"))
       cells (apply interleave item-separated)
       ](clojure.string/join "|" (cons columns-def cells))))

Still not quite sure about the way it handles column definitions but it seems to give the right output.