clojure iteratively destructure array of strings

Viewed 98

I'm learning clojure, and I want to take a vector of names, in last name -> first name order, of multiple people, and convert it to a vector of maps...

["Pan" "Peter" "Mouse" "Mickey"]

Should become...

[{:firstName Peter, :lastName Pan} {:firstName Mickey, :lastName Mouse}]

I've tried this, which doesn't work...

(for [[lastName firstName] 
      (list ["Pan" "Peter" "Mouse" "Mickey"])]
  {:firstName firstName, :lastName lastName}
  )

If I remove the list it turns the first/last name into individual characters.

I'm at a complete loss as to how to go about doing this.

3 Answers

You can convert your input vector into a sequence of pairs with partition:

(def names ["Pan" "Peter" "Mouse" "Mickey"])
(partition 2 names)

You can convert a pair of lastName/firstName into a map using zipmap e.g.

(zipmap [:lastName :firstName] ["Pan" "Peter"])

You can convert the sequences of pairs into a sequence of maps using map:

(map #(zipmap [:lastName :firstName] %) (partition 2 names))

Your first attempt was also pretty close. An example:

(ns tst.demo.core
  (:use tupelo.core tupelo.test))

(dotest
  (let [pairs  (partition 2 ["Pan" "Peter" "Mouse" "Mickey"])
        result (for [[lastName firstName] pairs]
                 {:firstName firstName, :lastName lastName})]
    (is= result [{:firstName "Peter", :lastName "Pan"}
                 {:firstName "Mickey", :lastName "Mouse"}])))

I usually prefer using for instead of map or mapv since it allows you to spread out the code a bit more. I think this is a bit more explicit to the reader.

Clojure Spec is perfect for these sorts of things. In your case, you would use the sequence operators to define a spec for your array, and then use conform:

(ns playground.arrayofstrings
  (:require [clojure.spec.alpha :as spec]))

(spec/def ::name-pairs (spec/* (spec/cat :lastName string?
                                         :firstName string?)))

(spec/conform ::name-pairs ["Pan" "Peter" "Mouse" "Mickey"])
;; => [{:lastName "Pan", :firstName "Peter"} {:lastName "Mouse", :firstName "Mickey"}]
Related