0
votes

I am exploring the exciting world of Clojure, but I am stopped on this... I have two vectors, different in length, stored in vars.

(def lst1 ["name" "surname" "age"]) 
(def lst2 ["Jimi" "Hendrix" "28" "Sam" "Cooke" "33" "Buddy" "Holly" "23"])

I want to interleave them and obtain a map, with keys from first list and values from the second, like the following one:

{"name" "Jimi" , "surname" "Hendrix" , "age" "28" , 
 "name" "Sam" , "surname" "Cooke" , "age" "33" ... }

even the following solution, with proper keys, would be ok:

{:name "Jimi" , :surname "Hendrix" , :age" "28" , ... }

I can use interleave-all function from Medley library and then apply the hash-map fn:

(apply hash-map 
    (vec (interleave-all (flatten (repeat 3 lst1)) lst2)))

=> {"age" "23", "name" "Buddy", "surname" "Holly"}

but returns just last musician. This persistent hashmap is not ordered, but is not the point.


I later tried to pair keys and values, maybe for a possible future use of assoc, who knows...

(map vector
       (for [numMusicians (range 0 3) , keys (range 0 3)] (-> lst1 (nth keys) (keyword)))
       (for [values (range 0 9)] (-> lst2 (nth values) (str)))
       )

Returns a lazy sequence with paired vectors and proper keywords.

=> ([:name "Jimi"] [:surname "Hendrix"] [:age "28"] [:name "Sam"] ...)

Now I want to try into that should return

a new coll consisting of to-coll with ALL of the items of from-coll conjoined.

(into {}
  (map vector
       (for [numMusicians (range 0 3) , keys (range 0 3)] (-> lst1 (nth keys) (keyword)))
       (for [values (range 0 9)] (-> lst2 (nth values) (str)))
       ))

But again:

=> {:name "Buddy", :surname "Holly", :age "23"}

just the last musician, this time in a persistent array map. I want a map with all my dead musicians. Someone knows where I am wrong?

Edit:

Thank you guys! Have managed the fn this way:

(use 'clojure.set)
(->> (partition 3 lst2) (map #(zipmap % lst1)) (map map-invert))

=> ({"name" "Jimi", "surname" "Hendrix", "age" "28"} {"name" "Sam", "surname" "Cooke", "age" "33"} {"name" "Buddy", "surname" "Holly", "age" "23"})
2

2 Answers

1
votes

Each key can only exist once in a map. So your later values overwrite the earlier ones.

To get a list of maps per artiste you could do something like:

(def lst1 ["name" "surname" "age"]) 
(def lst2 ["Jimi" "Hendrix" "28" "Sam" "Cooke" "33" "Buddy" "Holly" "23"])

(->> (partition 3 lst2) ; Split out the seperate people
     (map (fn [artist-seq] (zipmap lst1 artist-seq)))) ; Use zipmap to connect the keys and values.

This should work for any number of people as long as all the values are there, in the right order

1
votes

Although you want the following form:

{"name" "Jimi" , "surname" "Hendrix" , "age" "28" , 
 "name" "Sam" , "surname" "Cooke" , "age" "33" ... }

This is not allowed because keys are collided. You can't add "name" as a key several times. Key should be unique in a map.

But you can construct a list of map with the following code:

user=> (->> (map (fn [ks vs] (interleave ks vs)) (repeat 3 lst1) (partition 3 lst2))
            (map #(apply hash-map %)))

({"age" "28", "name" "Jimi", "surname" "Hendrix"} {"age" "33", "name" "Sam", "surname" "Cooke"} {"age" "23", "name" "Buddy", "surname" "Holly"})

UPDATE @status203's solution which uses zipmap looks much better.

user=> (->> (partition 3 lst2)
            (map #(zipmap lst1 %)))

({"age" "28", "surname" "Hendrix", "name" "Jimi"} {"age" "33", "surname" "Cooke", "name" "Sam"} {"age" "23", "surname" "Holly", "name" "Buddy"})