Keywordize with Java Maps - clojure

I'm using Nashorn in my Clojure application. When evaluating a javascript code it returns a ScriptObjectMirror which in turn implements the java.util.Map interface.
I'm trying to keywordize the maps keys using clojure.walk/keywordize-keys, however it seems to have no effect in maps other than IPersistentMap.
So I tried the follwing code:
(into {}
(for [[k v] eval-result]
[(keyword k) v]))
However it doesn't work recursively. Any idea on how to keywordize java maps? Shouldn't clojure.walk/keywordize-keys work with the Map interface?
Thanks.

I've found a way
(defn keywordize [object]
(if (instance? java.util.Map object)
(into {}
(for [[k v] object]
[(keyword k) (keywordize v)]))
object
)
)

Related

Return the key, where the value is a vector, when asking for a value that might be in that vector

Given the following data structure, I want to ask for "services-list" (a component) and receive back "entity-list" (a style).
(def style->components {"entity-list" ["services-list" "employee-list" "clients-list"]})
My solution is not so elegant:
(defn get-style-name [comp-name]
(-> (filter (fn [map-entry]
(let [v (val map-entry)
found-comp (some #(= % comp-name) v)]
found-comp
)) style->components)
first
first))
Is there a better way? Perhaps my problem started with the way I structured the data.
you can make it shorter and more clojurish this way:
(defn get-style-name [comp-name]
(ffirst (filter (fn [[_ v]]
(some #{comp-name} v))
component->style)))
there is a function ffirst, that works exactly like (first (first %))
using a destructuring in the filter function signature, you can retrieve the value of the map entry, avoiding unneeded let
instead of this function in some: #(= % comp-name) it is quite common to use the set: #{comp-name}
then you can use some instead of filter, as it returns the first logical true value returned by function, so you can remove ffirst:
(defn get-style-name [comp-name]
(some (fn [[k v]]
(when (some #{comp-name} v) k))
component->style))
also, if you change your data structure to use set instead of vector, you can make it even shorter:
(def component->style {"entity-list" #{"services-list"
"employee-list"
"clients-list"}})
(defn get-style-name [comp-name]
(some (fn [[k v]] (when (v comp-name) k))
component->style))
Just to add another alternative, nested sequence operations usually lend themselves to replacement with for:
(defn get-style-name
[comp-name]
(first
(for [[style-name comp-names] style->components
comp-name' comp-names
:when (= comp-name comp-name')]
style-name)))
Still, I'd prefer a solution where the mapping of component name to style name is pre-computed, e.g.
(def get-style-name
(->> (for [[style-name comp-names] style->components
comp-name comp-names]
[comp-name style-name])
(into {})))
This way, you avoid traversing the style->components map on every lookup.

ClojureScript - convert arbitrary JavaScript object to Clojure Script map

I am trying to convert a Javascript object to a Clojure. However, I get the following error :
(js/console.log (js->clj e)) ;; has no effect
(pprint (js->clj e)) ;; No protocol method IWriter.-write defined for type object: [object Geoposition]
Yes, this object comes from the Geolocation API. I suppose that I have to extend IEncodeClojure and IWriter, but I have no clue how.
For instance adding the following :
(extend-protocol IEncodeClojure
Coordinates
(-js->clj [x options]
(println "HERE " x options)))
Yields an error when loading my code : Uncaught TypeError: Cannot read property 'prototype' of undefined
The accepted answer wasn't working for me with the javascript object window.performance.timing. This is because Object.keys() doesn't actually return the props for the PerformanceTiming object.
(.keys js/Object (.-timing (.-performance js/window))
; => #js[]
This is despite the fact that the props of PerformanceTiming are indeed iterable with a vanilla JavaScript loop:
for (a in window.performance.timing) {
console.log(a);
}
// navigationStart
// unloadEventStart
// unloadEventEnd
// ...
The following is what I came up with to convert an arbitrary JavaScript object to a ClojureScript map. Note the use of two simple Google Closure functions.
goog.typeOf wraps typeof, which isn't normally accessible to us in ClojureScript. I use this to filter out props which are functions.
goog.object.getKeys wraps for (prop in obj) {...}, building up an array result which we can reduce into a map.
Solution (flat)
(defn obj->clj
[obj]
(-> (fn [result key]
(let [v (goog.object/get obj key)]
(if (= "function" (goog/typeOf v))
result
(assoc result key v))))
(reduce {} (.getKeys goog/object obj))))
Solution (recursive)
Update: This solution will work for nested maps.
(defn obj->clj
[obj]
(if (goog.isObject obj)
(-> (fn [result key]
(let [v (goog.object/get obj key)]
(if (= "function" (goog/typeOf v))
result
(assoc result key (obj->clj v)))))
(reduce {} (.getKeys goog/object obj)))
obj))
js->clj only works for Object, anything with custom constructor (see type) will be returned as is.
see: https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/core.cljs#L9319
I suggest doing this instead:
(defn jsx->clj
[x]
(into {} (for [k (.keys js/Object x)] [k (aget x k)])))
UPDATE for correct solution see Aaron's answer, gotta use goog.object
Two approaches that do not require writing custom conversion functions - they both employ standard JavaScript functions to loose the custom prototype and thus enable clj->js to work correctly.
Using JSON serialization
This approach just serializes to JSON and immediately parses it:
(js->clj (-> e js/JSON.stringify js/JSON.parse))
Advantages:
does not require any helper function
works for nested objects, with/without prototype
supported in every browser
Disadvantages:
performance might be a problem in critical pieces of codebase
will strip any non-serializable values, like functions.
Using Object.assign()
This approach is based on Object.assign() and it works by copying all the properties from e onto a fresh, plain (no custom prototype) #js {}.
(js->clj (js/Object.assign #js {} e))
Advantages:
does not require any helper function
Disadvantages:
works on flat objects, if there is another nested object withing e, it won't be converted by clj->js.
Object.assign() is not supported by old browsers, most notably - IE.
(defn obj->clj
([obj]
(obj->clj obj :keywordize-keys false))
([obj & opts]
(let [{:keys [keywordize-keys]} opts
keyfn (if keywordize-keys keyword str)]
(if (and (not-any? #(% obj) [inst? uuid?])
(goog.isObject obj))
(-> (fn [result k]
(let [v (goog.object/get obj k)]
(if (= "function" (goog/typeOf v))
result
(assoc result (keyfn k) (apply obj->clj v opts)))))
(reduce {} (.getKeys goog/object obj)))
obj))))
Small problem with the original above is that JS treats #inst and #uuid as objects. Seems like those are the only tagged literals in clojure
I also added the option to keywordize keys by looking at js->clj source

How to create a map inside a map?

I have a map containing emails and passwords that looks like this...
{"email1" "password1","email2" "password2","email3" "password3"}
I need to make a map inside a map that will contain email as the keyword(i think i explained it ok,i am a newbie in clojure).
How can i create a map with these values?
{"email1" {"email1" "password1"},"email2" {"email2" "password2"},"email3" {"email3" "password3"}}
I have tried zipmap but i get vector instead of a map...
{"email1" ["email1" "password1"],"email2" ["email2" "password2"],"email3" ["email3" "password3"]}
(def m {"email1" "password1","email2" "password2","email3" "password3"})
(into {} (map (fn [[k v]] [k {k v}]) m))
;= {"email2" {"email2" "password2"}, "email1" {"email1" "password1"}, "email3" {"email3" "password3"}}

Interop from clojure a with non-standard iterative java API

I am working in clojure with a java class which provides a retrieval API for a domain specific binary file holding a series of records.
The java class is initialized with a file and then provides a .query method which returns an instance of an inner class which has only one method .next, thus not playing nicely with the usual java collections API. Neither the outer nor inner class implements any interface.
The .query method may return null instead of the inner class. The .next method returns a record string or null if no further records are found, it may return null immediately upon the first call.
How do I make this java API work well from within clojure without writing further java classes?
The best I could come up with is:
(defn get-records
[file query-params]
(let [tr (JavaCustomFileReader. file)]
(if-let [inner-iter (.query tr query-params)] ; .query may return null
(loop [it inner-iter
results []]
(if-let [record (.next it)]
(recur it (conj results record))
results))
[])))
This gives me a vector of results to work with the clojure seq abstractions. Are there other ways to expose a seq from the java API, either with lazy-seq or using protocols?
Without dropping to lazy-seq:
(defn record-seq
[q]
(take-while (complement nil?) (repeatedly #(.next q))))
Instead of (complement nil?) you could also just use identity if .next does not return boolean false.
(defn record-seq
[q]
(take-while identity (repeatedly #(.next q))))
I would also restructure a little bit the entry points.
(defn query
[rdr params]
(when-let [q (.query rdr params)]
(record-seq q)))
(defn query-file
[file params]
(with-open [rdr (JavaCustomFileReader. file)]
(doall (query rdr params))))
Seems like a good fit for lazy-seq:
(defn query [file query]
(.query (JavaCustomFileReader. file) query))
(defn record-seq [query]
(when query
(when-let [v (.next query)]
(cons v (lazy-seq (record-seq query))))))
;; usage:
(record-seq (query "filename" "query params"))
Your code is not lazy as it would be if you were using Iterable but you can fill the gap with lazy-seq as follows.
(defn query-seq [q]
(lazy-seq
(when-let [val (.next q)]
(cons val (query-seq q)))))
Maybe you shoul wrap the query method to protect yourself from the first null value as well.

Clojure's maps: are keys and vals in same order?

Is it ok to rely on
(= m (zipmap (keys m) (vals m)))
in Clojure 1.3+?
Having this behavior makes for slightly more readable code in some situations, eg
(defn replace-keys [smap m]
(zipmap (replace smap (keys m)) (vals m)))
vs.
(defn replace-keys [smap m]
(into {} (for [[k v] m] [(smap k k) v]))
Yes, lots of clojure would break if that changed.
Maps are stored as trees and both functions walk the same tree in the same order.
I can confirm (officially) that the answer to this is yes. The docstrings for keys and vals were updated in Clojure 1.6 to mention this (see http://dev.clojure.org/jira/browse/CLJ-1302).