1
votes

I'm new to Clojure and playing with it for fun.

I'm reading a CSV file and want to apply a different function to each column. What is an elegant (both concise and readable) way to do this? I have explored two approaches:

  • Working on a vector of rows:

for each row...

(def row-1 ["John", "24"])

...I want to apply a different function to each element, and obtain this result:

["John", 24]

The function I want to use are:

(def converters-1 [identity, read-string])

Is there a simple way to apply the converters-1 functions to the row-1 elements?


  • Working on a map:

With this method I start by turning each row into a map:

(def row-2 {:name "John", :age "24"})

Edit: And I want to obtain this map:

{:name "John", :age 24}

The converters are also stored in a map:

(def converters-2 {:name identity, :age read-string})

Is there a simple way to apply the right converters to the row-2 elements?


I will be interested to read solutions for both approaches.

In the end I will put the data into a map. I'm just not sure whether I want to do the conversions before or after getting this map.

3

3 Answers

4
votes

Use map for sequences; use merge-with for maps.

user=> (map #(% %2) converters-1 row-1)
("John" 24)
user=> (merge-with #(% %2) converters-2 row-2)
{:name "John", :age 24}
1
votes
(map #(%1 %2) converters-1 row-1)
;; ("John" 24)

if (def row-2 {:name "John", :age "24"}) (it's an integer in your example)

(for [x (keys converters-2)] ((converters-2 x) (row-2 x)))
;; ("John" 24)
0
votes

I found a solution for the map approach, but it's less elegant than Diego's solution with vectors.

(into {} (map (fn [[k v]] 
    [k  ((converters-2 k) v)]
) row-2))

Is there a simpler solutions for this map-approach? Am I missing a core function that would simplify it?

Edit: reading Diego's edit, I could also use keys:

(into {} (map #( 
    [%  ((converters-2 %) (row-2 %))]   ; the key, and the converted value
) (keys row-2)))

But I prefer the previous solution, because it does not need the comment: it is obvious what happens to the key, and to the value. Plus, in this solution I only need to write row-2 once.

Edit 2: If I write (converters-2 k identity), then I only need to indicate the columns who need a transformation. For the other columns (like :name here) identity is the default converter. That is an advantage, compared to the vector approach.

Edit 3: I found a another solution for the map approach, using update-in:

(reduce #(update-in %1 [%2] (converters-2 %2)) row-2 (keys row-2))

Well, now that it's written, to my novice eyes it's harder to read and understand.

So far the vector solution is still best. I'll keep my eyes open for a better map solution; it might come in handy some day.