I'm calling a Javascript library and getting a Map Iterator back (which in Javascript you use with for ... in)
How do I turn this into a ClojureScript sequence so I can call for
on it?
I'm calling a Javascript library and getting a Map Iterator back (which in Javascript you use with for ... in)
How do I turn this into a ClojureScript sequence so I can call for
on it?
By looking at the MDN documentation on Map iterator you can check that you can navigate the iterator with the .next()
method which will return a JS object with 2 properties: value
and done
, where value
will contain an Array of size 2 with the key and value for each entry on the Map, and done
, which will be false until the iterator is exhausted (no more entries).
Using this, we can create a ClojureScript function that takes the iterator and builds a Map (or a ClojureScript map if you remove the clj->js
).
(defn ^:export iterToMap [iter]
(loop [acc {}]
(let [elem (.next iter)]
(if (.-done elem) ;; no more elements
(clj->js acc) ;; or just use `acc` for a regular CLJS map
(let [[k v] (array-seq (.-value elem))]
(recur (assoc acc k v)))))))
I used the ^:export
metadata flag so that you can call the function from the console. If you save this function in say app/main.cljs
, the function will be available as app.main.iterToMap()
in the global JS object.
You can use it as follows in the Developer Console:
> const map1 = new Map();
> map1.set("0", "zero");
> map1.set("1", "one");
// Now call the function, note that map1.entries() returns an Iterator:
> app.main.iterToMap(map1.entries());
// Returns:
Object { 0: "zero", 1: "one" }
If you want a sequence of key-value pairs, you can use the same principle to create a lazy-sequence, you just need to remember to hold a reference to the iterator so you can extract the next item.