Skip to content

Instantly share code, notes, and snippets.

@ertugrulcetin
Last active May 31, 2025 14:36
Show Gist options
  • Save ertugrulcetin/d666022d5cedc7139bdf06965f9b6e61 to your computer and use it in GitHub Desktop.
Save ertugrulcetin/d666022d5cedc7139bdf06965f9b6e61 to your computer and use it in GitHub Desktop.
ClojureScript Patch: assoc, dissoc, update-in for JS Objects
;; ClojureScript Patch: assoc, dissoc, update-in for JS Objects
;; Extends assoc, dissoc, and update-in to work seamlessly with native JavaScript objects in ClojureScript.
;; This namespace bridges the gap between idiomatic ClojureScript data manipulation and JavaScript interop, enabling structural updates on #js objects using familiar Clojure semantics.
(ns preload
(:refer-clojure :exclude [dissoc update-in assoc-in])
(:require
[applied-science.js-interop :as j]
[clojure.string :as str]))
(def kw->str
(memoize (fn [k]
(if (keyword? k)
(if (qualified-keyword? k)
(str (namespace k) "/" (name k))
(name k))
k))))
(def str->kw
(memoize (fn [str]
(if (string? str)
(if (str/includes? str "/")
(let [[f s] (str/split str #"/")]
(keyword f s))
(keyword str))
str))))
(extend-type object
ILookup
(-lookup
([o k]
(j/get o (kw->str k)))
([o k not-found]
(let [v (j/get o (kw->str k))]
(if (nil? v) not-found v))))
ISeqable
(-seq [o]
(when-let [keys (seq (js/Object.keys o))]
(map (fn [k]
[(str->kw k) (j/get o (kw->str k))])
keys)))
IAssociative
(-contains-key? [coll k]
(if (set? coll)
;; It's a workaround...
(-contains-key? (.-hash-map ^hash-map coll) k)
(j/call coll :hasOwnProperty (kw->str k))))
(-assoc [o k v]
(j/assoc! o (kw->str k) v))
ICounted
(-count [coll]
(j/get (js/Object.keys coll) :length)))
(extend-type function
ILookup
(-lookup
([o k]
(j/get o k))
([o k not-found]
(let [v (j/get o k)]
(if (nil? v) not-found v))))
IAssociative
(-contains-key? [coll k]
(j/call coll :hasOwnProperty (kw->str k)))
(-assoc [o k v]
(j/assoc! o (kw->str k) v)))
(extend-type array
ICollection
(-conj [coll o]
(j/call coll :push o))
ICounted
(-count [coll]
(j/get coll :length))
IAssociative
(-contains-key? [coll k]
(j/call coll :hasOwnProperty (kw->str k)))
(-assoc [o k v]
(j/assoc! o (kw->str k) v)))
(defn assoc-in [m [k & ks :as args] v]
(if (or (coll? m)
(and (not (coll? m))
(coll? (get-in m (drop-last args)))))
(if ks
(assoc m k (assoc-in (get m k) ks v))
(assoc m k v))
(j/assoc-in! m (map kw->str args) v)))
(set! cljs.core/assoc-in assoc-in)
(defn dissoc
([coll] coll)
([coll k]
(when-not (nil? coll)
(if (coll? coll)
(-dissoc coll k)
(do
(js-delete coll (kw->str k))
coll))))
([coll k & ks]
(when-not (nil? coll)
(let [ret (dissoc coll k)]
(if ks
(recur ret (first ks) (next ks))
ret)))))
(set! cljs.core/dissoc dissoc)
(defn update-in
([m [k & ks] f]
(let [r (get m k)
r (if (and (nil? r) (not (coll? m)) (> (count m) 0))
#js {}
r)]
(if ks
(assoc m k (update-in r ks f))
(assoc m k (f r)))))
([m [k & ks] f a]
(let [r (get m k)
r (if (and (nil? r) (not (coll? m)) (> (count m) 0))
#js {}
r)]
(if ks
(assoc m k (update-in r ks f a))
(assoc m k (f r a)))))
([m [k & ks] f a b]
(let [r (get m k)
r (if (and (nil? r) (not (coll? m)) (> (count m) 0))
#js {}
r)]
(if ks
(assoc m k (update-in r ks f a b))
(assoc m k (f r a b)))))
([m [k & ks] f a b c]
(let [r (get m k)
r (if (and (nil? r) (not (coll? m)) (> (count m) 0))
#js {}
r)]
(if ks
(assoc m k (update-in r ks f a b c))
(assoc m k (f r a b c)))))
([m [k & ks] f a b c & args]
(let [r (get m k)
r (if (and (nil? r) (not (coll? m)) (> (count m) 0))
#js {}
r)]
(if ks
(assoc m k (apply update-in r ks f a b c args))
(assoc m k (apply f r a b c args))))))
(set! cljs.core/update-in update-in)
(comment
(def data #js {})
(assoc data :a 1)
;;=> #js {:a 1}
(:a data)
;;=> 1
(update data :a inc)
;;=> #js {:a 2}
(assoc-in data [:c :d] 5)
(-> data :c :d)
;;=> 5
(assoc data :cljs-map {:b 1})
;;=> #js {:a 2 :cljs-map {:b 1}}
(update-in data [:cljs-map :b] inc)
;;=> #js {:a 2 :cljs-map {:b 2}}
(-> data :cljs-map :b)
;;=> 2
(seq #js {:a 1})
;;=> ([:a 1])
(dissoc #js {:a 1 :b 2} :a)
(def data-cljs {})
(assoc data-cljs :a 1)
;;=> {:a 1}
(update data-cljs :a inc)
;;=> {:a 1}
data-cljs
;;=> {}
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment