Given the recent additions to map destructuring, are there plans to support binding all extra keys that are not selected? E.g. similar to rest in:
(let [[a b & rest] a-seq] ,,,)
I am asking because we have custom code that I was thinking of extracting into a library, but would not bother if this was soon part of clojure.core.
If I understand correctly, the rest-map could be obtained like this in 1.13:
(let [{:keys [a b c] :as given :select selected} a-map
rest-map (apply dissoc given (keys selected))]
This is useful when a function takes an options map, wants to use some of the passed keys but pass the others on to another function.
In particular we use this for server-side HTML components with Hiccup:
(defn a-component [{:keys [a b c] :as attrs}]
;; Treat any extra attributes as HTML attributes and pass them to hiccup to render as HTML attributes:
[:div (dissoc attrs :a :b :c) ,,,])
(a-component {:a 42, :class "green}) ;; => [:div {:class "green"} ,,,]
Would be nice if I could do this:
(defn a-component [{:keys [a b c] :rest extra}]
[:div extra ,,,])
Similar to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring#rest_properties_and_rest_elements`…rest`https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring#rest_properties_and_rest_elements:
const { a, b, ...rest } = obj;What about nested data? :select is a deep nested merge of keys and defaults, as is :all
yes, will do nested like the others, else trivial as above
user=> (pprint
(let [{{:keys [p q r] :or {r 99}}:nested
a :a :keys! [b c & :d :e :f] :as m :or {a 42 }
:defaults defs :select sel :all all :rest extra}
{:aa 1 :b 2 :c 3 :d 4 :e 5 :f 6 :x 42 :nested {:q 2 :zz :top}}]
{:a a :b b :c c :select sel :as m :defs defs :all all :rest extra}))
{:a 42,
:b 2,
:c 3,
:select {:nested {:q 2, :r 99}, :e 5, :c 3, :b 2, :d 4, :f 6, :a 42},
:as
{:aa 1,
:b 2,
:c 3,
:d 4,
:e 5,
:f 6,
:x 42,
:nested {:q 2, :zz :top}},
:defs {:a 42},
:all
{:a 42,
:aa 1,
:b 2,
:c 3,
:d 4,
:e 5,
:f 6,
:x 42,
:nested {:r 99, :q 2, :zz :top}},
:rest {:aa 1, :x 42, :nested {:zz :top}}}
predicative, so if there's nothing extra :rest will be nil
unsure about :rest` `atm
vs :resid (ual) :extra etc
Naming is hard:grin:
note that above the :rest result contains the key :nested, which is part of the named/select set, because the nested map contains extra keys
that adds some nuance in practice, since not all the top-level keys in :rest are necessarily 'foreign' when nesting is involved
i think in javascript the ...rest syntax only grabs non-specified keys from that specific level, for comparison
figures, don't care about JS. In any case that's trivial and not worth adding. Deep is the point of doing these things, and you want rest == all - select (map math)
the above code is already working
:more :diff...
liking :diff the most atm
it's the difference between what you specified and what you got
I'm excited for this. I'm always hitting situations where I have to decide between using a flat map that accurately represents the thing i want, or adding some nesting only because at some point (after processing some specific keys) the data is sent to some non-open-world system, and selecting/dissoc'ing things manually from the flat version would be harder to maintain.
the reason I'm adding these things to destructuring is it already is doing a recursive descent - doing it post would be a tree walk/rebuild, both a hassle and error prone
This is nice.
I’d vote for :extra rather than :diff. To me it feels natural to think that I’m receiving extra stuff; “diff” doesn’t express that so clearly IMO.
Cool! Thanks for the info!
but if we ever has a feature that injected data in destructuring... (and no, not thinking about that)
:extra {:more stuff ...}
:no-select ?
:sans ?
:& would mirror vector destructuring
& already means 'more keys' now in :keys et al
dang that's true
it's really set/map difference
Should :all be called :union then?
different sets (input + defaults)
this is a diff between specified and provided
:all is all the info you've got, between the code and the input
Makes sense
thinking out loud, :rest would mirror/reuse the "rest args"/rest destructuring terminology, :else is the community standard cond final branch, :diff matches the set/map difference terminology
I was skeptical about :diff at first, but thinking about nesting and what :select does, it seems pretty fitting to me now
:diff seems fine to me. No single name is going to satisfy everyone (people love to bike shed on names!).
My 2 cents: :remainder
Maybe :rem
Even :others i think that may works :)
To check my understanding because there are multiple sets in play (maybe helpful for others as well):
{:as input
:defaults defaults ;; :or, shallow
:all input+defaults ;; union, deep (includes nested defaults)
:select specified ;; :keys et al, subset of :all, deep
:diff input-specified ;; difference, deep
}:defaults should be considered separately. The others are all 'views' of the input. :defaults is just a decoding (to key->val) of what you wrote in :or. Different category entirely.
in the light of auto completion and AI I'm not a fan of artificial abbreviations like :rem or :resid. It only adds mental load IMHO.