Records as map keys in clojurescript - clojure

(defrecord Pot [a])
(def p (Pot. 1))
(def m {p 2})
(m p) ; Returns 2 in both Clojure and Clojurescript
(m (Pot. 1)) ; Returns 2 in Clojure and nil/null in Clojurescript
In Clojure, looking up a value either with the original key, or a newly constructed key returns the desired value. However, in Clojurescript, with a newly constructed key it returns null/nil. What's the best way to make the lookup work in Clojurescript as it does in Clojure?

It looks like I have to implement equals myself for records in clojurescript:
(defrecord Pot [a])
(extend-type Pot
IEquiv
(-equiv [this that] (and (instance? Pot that) (= (into {} this) (into {} that)))))
Works fine with this. I would have thought Clojurescript would implement equals for records by default.

Related

Clojure call series of functions and store their return values

I'm building a datomic schema and have the following at the foot of my clj file which defines and transacts schema and initial data. The functions being called below each call d/transact.
(defn recreate-database []
"To recreate db after running delete-database in bin/repl"
(pt1-transact-schema)
(pt1-transact-data)
(pt2-transact-schema)
(pt2-transact-data)
(pt3-transact-schema)
(pt3-transact-data))
By default we only see the return value of the last form, but I'd like to see, or save, the result of each of the six function calls.
Wondering what a nice way to do this is.
Thought of something like (map (comp println eval) [functions]), but that's not right.
there is also a nice functional composition function called juxt:
user> ((juxt + - * /) 1 2)
;;=> [3 -1 2 1/2]
user> ((juxt (constantly 1) (constantly 2) (constantly 3)))
;;=> [1 2 3]
or in your case:
(def recreate-database (juxt pt1-transact-schema
pt1-transact-data
pt2-transact-schema
pt2-transact-data
pt3-transact-schema
pt3-transact-data))
You could try this:
(defn recreate-database []
"To recreate db after running delete-database in bin/repl"
(mapv #(%) [pt1-transact-schema
pt1-transact-data
pt2-transact-schema
pt2-transact-data
pt3-transact-schema
pt3-transact-data]))
The expression #(%) is a shorthand notation for a lambda function that takes one argument, representing a function, and calls that function. If you find it more readable, you can replace that expression by (fn [f] (f)).
With datomic, all you need is a connection and a list of tx-data. Then you can use map to return the transact result on each step (i.e. each tx-data):
(defn recreate-database [conn & tx-data]
(->> tx-data
(map (partial d/transact conn))
doall))

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

Assoc-if in Clojure

Is there an "assoc-if" function in the Clojure library? I.e if a value is truthy, update a map with a given key value. I've tried to find something to this effect, but came up lacking.
(defn assoc-if
[m key value]
(if value (assoc m key value) m))
If the goal is to avoid repeating m, you can use conj:
(conj m (when value [key value]))
...or Clojure 1.5's new threading macros:
(-> m
(cond-> value (assoc key value)))
If it's actually important to avoid repeating both the m and value, you'll have to write your own or reach outside clojure.core
There is no build-in assoc-if function in Clojure, but you're not the first one who needs it. Check this link with an implementation of assoc-if by ALEX MILLER:
(defn ?assoc
"Same as assoc, but skip the assoc if v is nil"
[m & kvs]
(->> kvs
(partition 2)
(filter second)
flatten
(apply assoc m)))
But, since flatten is recursive, it's best to replace it with something which is not (thanks to kotarak for the hint). Another problem of this implementation is that (apply assoc m) will fail on empty list. So, it's best to replace it to:
(defn ?assoc
"Same as assoc, but skip the assoc if v is nil"
[m & kvs]
(->> kvs
(partition 2)
(filter second)
(map vec)
(into m)))
Just use assoc-some from the medley library. Battle-tested and widely used.

what advantage is there to use 'get' instead to access a map

Following up from this question: Idiomatic clojure map lookup by keyword
Map access using clojure can be done in many ways.
(def m {:a 1}
(get m :a) ;; => 1
(:a m) ;; => 1
(m :a) ;; => 1
I know I use mainly the second form, and sometimes the third, rarely the first. what are the advantages (speed/composability) of using each?
get is useful when the map could be nil or not-a-map, and the key could be something non-callable (i.e. not a keyword)
(def m nil)
(def k "some-key")
(m k) => NullPointerException
(k m) => ClassCastException java.lang.String cannot be cast to clojure.lang.IFn
(get m k) => nil
(get m :foo :default) => :default
From the clojure web page we see that
Maps implement IFn, for invoke() of one argument (a key) with an
optional second argument (a default value), i.e. maps are functions of
their keys. nil keys and values are ok.
Sometimes it is rewarding to take a look under the hoods of Clojure. If you look up what invoke looks like in a map, you see this:
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/APersistentMap.java#L196
It apparently calls the valAt method of a map.
If you look at what the get function does when called with a map, this is a call to clojure.lang.RT.get, and this really boils down to the same call to valAt for a map (maps implement ILookUp because they are Associatives):
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/RT.java#L634.
The same is true for a map called with a key and a not-found-value. So, what is the advantage? Since both ways boil down to pretty much the same, performance wise I would say nothing. It's just syntactic convenience.
You can pass get to partial etc. to build up HOFs for messing with your data, though it doesn't come up often.
user=> (def data {"a" 1 :b 2})
#'user/data
user=> (map (partial get data) (keys data))
(1 2)
I use the third form a lot when the data has strings as keys
I don't think there is a speed difference, and even if that would be the case, that would be an implementation detail.
Personally I prefer the second option (:a m) because it sometimes makes code a bit easier on the eye. For example, I often have to iterate through a sequence of maps:
(def foo '({:a 1} {:a 2} {:a 3}))
If I want to filter all values of :a I can now use:
(map :a foo)
Instead of
(map #(get % :a) foo)
or
(map #(% :a) foo)
Of course this is a matter of personal taste.
To add to the list, get is also useful when using the threading macro -> and you need to access via a key that is not a keyword
(let [m {"a" :a}]
(-> m
(get "a")))
One advantage of using the keyword first approach is it is the most concise way of accessing the value with a forgiving behavior in the case the map is nil.

Pre-aggregated datastructure in clojure

In OLAP-cubes it's possible to do very quick look ups on large amounts of aggregated data. The major reason for this is that one pre-aggregates data in operations which are easy to combine upwards (mainly +, -, mean, std, max, min and some more).
How to get this "anti-lazy" behaviour in clojure?
I'm thinking on something like
(def world-population {:africa 4e8 ;;this is an aggregation!
:africa/liberia 3.4e6
:africa/ethiopia 7.4e7
...})
How to update a datastructure like this and make sure the parents of an entity is updated too? Do one have to roll one's own ref-implementation?
By storing your data in an atom, you can add watches - essentially callbacks when the atom is updated
Something like this:
(def world-population (atom {:africa 4e8
:africa/liberia 3.4e6
...}))
(add-watch word-population :population-change-key
(fn [key ref old new]
(prn "population change")))
You could build some event propagation logic on top of that.
You could write a recursive rollup function as a higher order function, something like:
(defn rollup
([data heirarchy func]
(loop [top (second (first heirarchy))]
(if (nil? (heirarchy top))
(rollup data heirarchy func top)
(recur (heirarchy top)))))
([data heirarchy func root]
(let [children (reduce (fn [l [k v]] (if (= v root) (cons k l) l)) '() heirarchy)
data (reduce (fn [d c] (if (d c) d (rollup d heirarchy func c))) data children)
child-values (map data children)]
(assoc data root (apply func child-values)))))
Which can then be used with any particular rollup operation or hierarchy you like:
(def populations { :africa/liberia 3.4e6
:africa/ethiopia 7.4e7})
(def geography {:africa/liberia :africa
:africa/ethiopia :africa
:africa :world})
(rollup populations geography +)
=> {:africa 7.74E7,
:world 7.74E7,
:africa/ethiopia 7.4E7,
:africa/liberia 3400000.0}
Obviously it gets more complicated if you have very large data sets or multiple hierarchies etc., but this should be enough for many simple cases.