2

I have a sequence of sequences, output from clojure-csv.

(def s1 [[:000-00-0000 "SMITH" "JOHN" 27][:000-00-0001 "FARMER" "FANNY" 28]])

I have a vector of column numbers [0 3] that I want to use to extract data from each sequence.

Rather than writing a function to zip together a variable number of map forms, I thought a macro might do the trick. But, I am having trouble.

This macro accepts a sequence and a column "mask"

   (defmacro map-cols [seq & columns]
    (for [col# columns
        :let [mc# `(map #(nth % ~col# nil) ~seq)]]
        mc#))

(map-cols s1 cols)
ClassCastException clojure.lang.LazySeq cannot be cast to clojure.lang.IFn  bene-csv.core/eval2168 

I was hoping to generate the multiple map forms that show up in the following:

(zipmap (map #(nth % 0 nil) s1) (map #(nth % 1 nil) s1))
{:000-00-0001 "FARMER", :000-00-0000 "SMITH"}

I would appreciate some ideas of what I am doing wrong. I can, of course, just tailor the a function to the number of columns I need to extract.

Thank You.

Edit:

Modified macro

(defmacro map-cols [seq & columns]
    (vec (for [col columns
        :let [mc `(map #(nth % ~col nil) ~seq)]]
        mc)))


(apply zipmap (map-cols s1 cols))

ArityException Wrong number of args (1) passed to: core$zipmap  clojure.lang.AFn.throwArity

1 Answer 1

3

You are mixing up code that will execute at macro expansion with code that the macro will output. Until you enter a syntax-quote, you don't need to use auto-gensym (col#, mc# in your example). As for the macro output, it must always produce exactly one form. It seems that you are expecting it to produce several forms (one for each col), but that's not how this can work. Your macro output will currently look like

((map #(nth % 0 nil) s1) (map #(nth % 1 nil) s1))

This is a form with two members. The member in the head position is expected to be a function, and the whole form is supposed to be evaluated as a function call.

The way to salvage this is to wrap your for in the macro with a vec and then use (apply zipmap (map-cols s1 cols)).

That answers your immediate question, but the solution will still not make sense: zipmap wants exactly two args, not a variable number of them as you said you want, and the output is a map, not a sequence that zips together your fields. Zipping is achieved using (map vector seq1 seq2 ...).

Sign up to request clarification or add additional context in comments.

4 Comments

I wasn't quite sure what edits you wanted me to make, so I edited original question and output. Thanks.
A style remark: your :let [mc ...] is superfluous. Just produce the result directly: (for [col columns] `(map #(nth % ~col nil) ~seq)). As for your new problem, as I said, zipmap expects exactly two args. You are giving it, as it currently stands, only one because you invoke your macro with only one column.
Sorry, I can't compile the changes.(defmacro map-cols [seq & columns] (vec (for [col columns `(map #(nth % ~col nil) ~seq)])))
You've placed the right brackt wrong (it should be after columns). Compare with mine.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.