In clojurescript when I merge two maps I can do it like this:
(merge {:a 1} {:b 2})
How can I achieve a similar thing in Om, where literals are used, something like this (which doesn't work):
(merge #js {:a 1} #js {:b 2})
You can merge them as you normally and then use clj->js to convert the result into javascript:
(clj->js (merge {:a 1} {:b 2}))
Related
This must be one of those silly/complex things that everybody founds when learning a new framework. So I have this function:
(defn display-questions-list
[]
(let [counter (atom 1)]
[:div
(doall (for [question #(rf/subscribe [:questions])]
^{:key (swap! counter inc)} [question-item (assoc question :counter #counter)])])))
The #counter atom doesn't hold any important data, it's just a "visual" counter to display the number in the list. When the page is loaded for first time, all works fine, if there are five questions the list displays (1..5), the issue is that when a question is created/edited/deleted the subscription:
#(rf/subscribe [:questions])
is called again and then of course the list is displayed but now from 6 to 11. So I need a way to reset the #counter.
You should not be using an atom for this purpose. Your code should look more like this:
(ns tst.demo.core
(:use tupelo.test)
(:require [tupelo.core :as t]))
(defn display-questions-list
[]
[:div
(let [questions #(rf/subscribe [:questions])]
(doall (for [[idx question] (t/indexed questions)]
^{:key idx}
[question-item (assoc question :counter idx) ])))])
The tupelo.core/indexed function from the Tupelo library simply prepends a zero-based index value to each item in the collection:
(t/indexed [:a :b :c :d :e]) =>
([0 :a]
[1 :b]
[2 :c]
[3 :d]
[4 :e])
The source code is pretty simple:
(defn zip-lazy
"Usage: (zip-lazy coll1 coll2 ...)
(zip-lazy xs ys zs) => [ [x0 y0 z0]
[x1 y1 z1]
[x2 y2 z2]
... ]
Returns a lazy result. Will truncate to the length of the shortest collection.
A convenience wrapper for `(map vector coll1 coll2 ...)`. "
[& colls]
(assert #(every? sequential? colls))
(apply map vector colls))
(defn indexed
"Given one or more collections, returns a sequence of indexed tuples from the collections:
(indexed xs ys zs) -> [ [0 x0 y0 z0]
[1 x1 y1 z1]
[2 x2 y2 z2]
... ] "
[& colls]
(apply zip-lazy (range) colls))
Update
Actually, the main goal of the :key metadata is to provide a stable ID value for each item in the list. Since the items may be in different orders, using the list index value is actually a React antipattern. Using a unique ID either from within the data element (i.e. a user id, etc) or just the hashcode provides a unique reference value. So, in practice your code would be better written as this:
(defn display-questions-list
[]
[:div
(doall (for [question #(rf/subscribe [:questions])]
^{:key (hash question)}
[question-item (assoc question :counter idx)]))])
Some hashcode samples:
(hash 1) => 1392991556
(hash :a) => -2123407586
(hash {:a 1, :b [2 3 4]}) => 383153859
Which form is better?
(defn my-func [arg arg2]
(fn [] [:div (str arg arg2)]))
Or this one?
(defn my-func []
(fn [arg arg2] [:div (str arg arg2)]))
I'm writting from my phone so forgive me indentation...
There's other difference between these functions aside from arity.
The first function will close over arg, and arg2 on first render, and use these arguments for each subsequent rerender. E.g.
(def a 1)
(def b 2)
[my-func a b]
will always result in [:div '12'] whether or not a or b were changed
The second function will take arg, and arg2 on each render and will update the result when arguments change.
Look for Rookie mistake in reagent docs
I believe your question should be which one is correct(The first one is correct). The second one has got an arity exception. Based on your use case, I would prefer to define and call the anonymous function so as to get the results. Consider this number one edit:
(defn my-func [arg arg2]
((fn [] [:div (str arg arg2)])))
I am using clojurescript 0.0-2371 and I am trying to write some code that will clone an object. I have this code where I want to clone a node and calls a clone-object function:
(def animate
(js/React.createClass
#js
{:getInitialState
(fn []
(this-as this
{:children
(->
(.. this -props -children)
(js/React.Children.map (fn [child] child))
(js->clj :keywordize-keys false))}))
:render
(fn []
(this-as this
(let [children (:children (.. this -state))]
(doseq [[k v] children]
(clone-object (aget children k))))))}))
clone-object looks like this:
(defn clone-object [obj]
(log/debug obj)
(doseq [[k v] obj]
(log/debug k)))
And if I call clone-object like this:
(doseq [[k v] children]
(clone-object v))
I get this error:
Uncaught Error: [object Object] is not ISeqable
The answer was to use goog.object.forEach:
(defn clone-object [key obj]
(goog.object/forEach obj
(fn [val key obj]
(log/debug key))))
You don't strictly need Google Closure for looping through keys:
(defn obj->vec [obj]
"Put object properties into a vector"
(vec (map (fn [k] {:key k :val (aget obj k)}) (.keys js/Object obj)))
)
; a random object
(def test-obj (js-obj "foo" 1 "bar" 2))
; let's make a vector out of it
(def vec-obj (obj->vec test-obj))
; get the first element of the vector and make a key value string
(str (:key (first vec-obj)) ":" (:val (first vec-obj)))
About obj->vec:
vec convert a sequence to a vector (optional in case you are ok with a sequence)
map execute (fn [k] ...) for each element of the list.
(fn [k] ...) Take the k element of the list and put it in a key/value map data structure, aget takes the obj property referred by the key k.
(.keys js/Object obj) Get all the keys from a js object.
I'm trying to figure why this particular function isn't working as expected. I suspect from the error message that it has something to do with the way I'm creating the empty vector for the accumulator.
I have a simple function that returns a sequence of 2-element vectors:
(defn zip-with-index
"Returns a sequence in which each element is of the
form [i c] where i is the index of the element and c
is the element at that index."
[coll]
(map-indexed (fn [i c] [i c]) coll))
That works fine. The problem comes when I try to use it in another function
(defn indexes-satisfying
"Returns a vector containing all indexes of coll that satisfy
the predicate p."
[p coll]
(defn accum-if-satisfies [acc zipped]
(let [idx (first zipped)
elem (second zipped)]
(if (p elem)
(conj acc idx)
(acc))))
(reduce accum-if-satisfies (vector) (zip-with-index coll)))
It compiles, but when I attempt to use it I get an error:
user=> (indexes-satisfying (partial > 3) [1 3 5 7])
ArityException Wrong number of args (0) passed to: PersistentVector
clojure.lang.AFn.throwArity (AFn.java:437)
I can't figure out what's going wrong here. Also if there is a more 'Clojure-like' way of doing what I'm trying to do, I'm interested in hearing about that also.
The problem is probably on the else clause of accum-if-satisfies, should be just acc not (acc).
You could use filter and then map instead of reduce. Like that:
(map #(first %)
(filter #(p (second %))
(zip-with-index coll)))
You could also call map-indexed with vector instead of (fn [i c] [i c]).
The whole code would look like that:
(defn indexes-satisfying
[p coll]
(map #(first %)
(filter #(p (second %))
(map-indexed vector coll))))
As for a more Clojure-like way, you could use
(defn indexes-satisfying [pred coll]
(filterv #(pred (nth coll %))
(range (count coll))))
Use filter instead of filterv to return a lazy seq rather than a vector.
Also, you should not use defn to define inner functions; it will instead define a global function in the namespace where the inner function is defined and have subtle side effects besides that. Use letfn instead:
(defn outer [& args]
(letfn [(inner [& inner-args] ...)]
(inner ...)))
One more way to do it would be:
(defn indexes-satisfying [p coll]
(keep-indexed #(if (p %2) % nil) coll))
I would like to be able to use (js-obj) like a hash-map so that
(def a (js* "{'a': 1, 'b':2}"))
(a :a)
;=> 1
I tried doing this:
(extend-type object
IFn
(-invoke
([o k] (aget o (strkey k))))
but it didn't work.
You can use use reify. Note that means you won't be able to access the properties directly then:
(defn convert [obj]
(reify
IFn
(-invoke [obj]
([o k] (aget o (strkey k))))))
Of course you could write a protocol so you can access the original object if you like.