fn-in.core
This namespace provides functions to:
- inspect an element (
get
) - change an element (
assoc
) - apply a function to an element (
update
) - remove an element (
dissoc
)
from any of Clojure’s collection types (plus non-terminating sequences), similarly to their clojure.core
namesakes. The ...-in
variants operate on any heterogeneous, arbitrarily-nested data structure.
- Elements contained in vectors, lists, and other sequences are addressed by zero-indexed integers.
- Elements contained in maps are addressed by key, which may be any valid Clojure value, including a composite.
- Elements contained in sets are addressed by the element’s value, which may be a composite.
Conventions:
c
collectioni
index/keyx
valuef
functionarg1
,arg2
,arg3
optional args to multi-arity functionf
.
assoc*
multimethod
(assoc* c i val)
Returns a new collection c
with the key/index i
associated with the supplied value val
. Similar to clojure.core/assoc
, but operates on all Clojure collections. Indexes beyond the end of a sequence are padded with nil
.
Note: Because set members are addressed by their value, the assoc*
-ed value may match a pre-existing set member, and the returned set may have one fewer members.
Examples:
(assoc* [11 22 33] 1 99) ;; => [11 99 33]
(assoc* {:a 11 :b 22} :b 99) ;; => {:a 11, :b 99}
(assoc* (list 11 22 33) 1 99) ;; => (11 99 33)
(assoc* #{11 22 33} 22 99) ;; => #{99 33 11}
(assoc* (range 3) 1 99) ;; => (0 99 2)
(assoc* (take 6 (iterate dec 10)) 3 99) ;; => (10 9 8 99 6 5)
;; associating an existing set member reduces the size of the set
(assoc* #{11 22 33} 22 33) ;; => #{33 11}
;; associating a value into a non-terminating sequence
(take 5 (assoc* (repeat 3) 2 99)) ;; => (3 3 99 3 3)
;; associating a value beyond a sequence's bounds causes nil-padding
(assoc* [11 22 33] 5 99) ;; => (11 22 33 nil nil 99)
assoc-in*
(assoc-in* c [i & i_s] x)
Returns collection c
with a new value x
associated at path vector of i
elements. Similar to clojure.core/assoc-in
, but operates on any heterogeneous, arbitrarily-nested collections. Supplying an empty path throws an exception. Associating beyond the end of sequence results in nil
-padding.
Examples:
(assoc-in* [11 22 33 [44 55 [66 77]]] [3 2 1] :foo) ;; => [11 22 33 [44 55 [66 :foo]]]
(assoc-in* {:a {:b {:c 42}}} [:a :b :c] 99) ;; => {:a {:b {:c 99}}}
(assoc-in* (list 11 22 [33 44 (list 55)]) [2 1] :foo) ;; => (11 22 [33 :foo (55)])
(assoc-in* #{11 [22 #{33}]} [[22 #{33}] 1 33] :hello) ;; => #{11 [22 #{:hello}]}
;; heterogeneous, nested collections
(assoc-in* [11 {:a [22 {:b 33}]}] [1 :a 1 :b] 42) ;; => [11 {:a [22 {:b 42}]}]
(assoc-in* {'foo (list 11 {22/7 'baz})} ['foo 1 22/7] :new-val) ;; => {foo (11 {22/7 :new-val})}
;; associating beyond nested sequence's bounds causes nil-padding
(assoc-in* [11 22 [33 44]] [2 3] 99) ;; => [11 22 (33 44 nil 99)]
;; associating a non-existent key-val in a map merely expands the map
(assoc-in* {:a 11 :b {:c 22}} [:b :d] 99) ;; => {:a 11, :b {:c 22, :d 99}}
dissoc*
multimethod
(dissoc* c i)
Returns a new collection c
with the value located at key/index i
removed. Similar to clojure.core/dissoc
, but operates on all Clojure collections. i
must be within the bounds of a a sequence. If element at i
does not exist in a map or set, the new returned collection is identical. Similarly, dissoc
-ing an element from a clojure.lang.Repeat
returns an indistinguishable sequence.
Examples:
(dissoc* [11 22 33] 1) ;; => [11 33]
(dissoc* {:a 11 :b 22} :a) ;; => {:b 22}
(dissoc* (list 11 22 33) 1) ;; => (11 33)
(dissoc* #{11 22 33} 22) ;; => #{33 11}
(dissoc* (take 3 (cycle [:a :b :c])) 1) ;; => (:a :c)
;; non-existent entity
(dissoc* #{11 22 33} 99) ;; => #{33 22 11}
(dissoc* {:a 11 :b 22} :c) ;; => {:a 11, :b 22}
dissoc-in*
(dissoc-in* c [i & i_s])
Remove element located at i
from an arbitrarily-nested collection c
. Any containing collections are preserved if i
addresses a single scalar. If i
addresses a nested collection, all children are removed.
Examples:
(dissoc-in* [11 [22 [33 44]]] [1 1 0]) ;; => [11 [22 [44]]]
(dissoc-in* {:a 11 :b {:c 22 :d 33}} [:b :c]) ;; => {:a 11, :b {:d 33}}
(dissoc-in* (list 11 (list 22 33)) [1 0]) ;; => (11 (33))
(dissoc-in* #{11 [22 33]} [[22 33] 0]) ;; => #{[33] 11}
(dissoc-in* [11 (range 4)] [1 2]) ;; => [11 (0 1 3)]
;; heterogeneous, nested collections
(dissoc-in* [11 {:a (list 22 [33 44])}] [1 :a 1 0]) ;; => [11 {:a (22 [44])}]
(dissoc-in* {:a 11 :b [22 #{33 44}]} [:b 1 33]) ;; => {:a 11, :b [22 #{44}]}
;; dissociating an element that is itself a nested collection; containers are dissociated
(dissoc-in* [11 [22]] [1]) ;; => [11]
(dissoc-in* {:a {:b {:c 99}}} [:a :b]) ;; => {:a {}}
;; dissociating a scalar element; containers are preserved
(dissoc-in* [11 [22 [33]]] [1 1 0]) ;; => [11 [22 []]]
(dissoc-in* [11 {:a 22}] [1 :a]) ;; => [11 {}]
(dissoc-in* [11 22 #{33}] [2 33]) ;; => [1 2 #{}]
See also:
get*
multimethod
(get* c i)
Inspect the value at location i
within a collection c
. Similar to clojure.core/get
. Returns nil
if not found.
Note: get*
does not offer a not-found
arity.
Examples:
(get* [11 22 33 44 55] 2) ;; => 33
(get* {:x 11 :y 22 :z 33} :y) ;; => 22
(get* (list 11 22 33 44 55) 2) ;; => 33
(get* #{11 22 33} 22) ;; => 22
(get* (range 99) 3) ;; => 3
(get* (cycle [11 22 33]) 5) ;; => 33
;; non-keyword key
(get* {[11 22 33] 'val-1 99 'val-2} 99) ;; => val-2
get-in*
(get-in* c path)
Inspects the value in heterogeneous, arbitrarily-nested collection c
at path
, a vector of indexes/keys. This version of clojure.core/get-in
operates on all Clojure collections. An empty path
vector returns the original collection c
. Performance is not optimized, so it might steal your lunch money.
Examples:
(get-in* [11 22 33 [44 [55]]] [3 1 0]) ;; => 55
(get-in* {:a 11 :b {:c 22 :d {:e 33}}} [:b :d :e]) ;; => 33
(get-in* (list 11 22 [33 (list 44 (list 55))]) [2 1 1 0]) ;; => 55
(get-in* #{11 [22 [33]]} [[22 [33]] 1 0]) ;; => 33
;; empty path addresses root collection
(get-in* [11 22 33] []) ;; => [11 22 33]
;; heterogeneous, nested collections; return may be a collection
(get-in* {:a [11 22 {:b [33 [44] 55 [66]]}]} [:a 2 :b 3]) ;; => [66]
;; address of a nested set; compare to next example
(get-in* [11 {:a [22 #{33}]}] [1 :a 1 ]) ;; => #{33}
;; address of an element contained within a nested set; compare to previous example
(get-in* [11 {:a [22 #{33}]}] [1 :a 1 33]) ;; => 33
;; non-terminating sequence
(get-in* (repeat [11 22 33]) [3 1]) ;; => 22
;; nested non-terminating sequences
(get-in* (repeat (cycle [:a :b :c])) [99 5]) ;; => :c
update*
(update* c i f)
(update* c i f arg1)
(update* c i f arg1 arg2)
(update* c i f arg1 arg2 arg3)
(update* c i f arg1 arg2 arg3 & more)
Returns a new collection c
with function f
applied to the value at location i
. If the location doesn’t exist, nil
is passed to f
. Additional arguments args
may be supplied trailing f
. Similar to clojure.core/update
, but operates on all Clojure collections.
Note: Because set members are addressed by their value, the update*
-ed value may match a pre-existing set member, and the returned set may have one fewer members.
Examples:
(update* [10 20 30] 1 dec) ;; => [10 19 30]
(update* {:a 10} :a dec) ;; => {:a 9}
(update* (list 10 20 30) 1 dec) ;; => (10 19 30)
(update* #{10 20 30} 20 dec) ;; => #{19 30 10}
;; function handles nil if no value exists
(update* [11 22 33] 4 (constantly :updated-val)) ;; => (11 22 33 nil :updated-val)
;; additional args
(update* {:a 99} :a #(/ %1 %2) 9) ;; => {:a 11}
;; update absorbs existing set member resulting in a smaller set
(update* #{32 33} 33 dec) ;; => #{32}
update-in*
(update-in* m ks f & args)
Returns a new collection c
with the value at path vector ks
updated by applying function f
to the previous value. Similar to clojure.core/update-in
, but operates on any heterogeneous, arbitrarily-nested collection. Additional arguments args
may be supplied trailing f
. If location ks
does not exist, f
must handle nil
.
Note: Updating a set member to another previously-existing set member will decrease the size of the set.
Examples:
(update-in* [11 [22 [33]]] [1 1 0] inc) ;; => [11 [22 [34]]]
(update-in* {:a {:b {:c 99}}} [:a :b :c] inc) ;; => {:a {:b {:c 100}}}
(update-in* (list 11 [22 (list 33)]) [1 1 0] inc) ;; => (11 [22 (34)])
(update-in* #{11 [22 #{33}]} [[22 #{33}] 1 33] inc) ;; => #{[22 #{34}] 11}
;; heterogeneous nested collections
(update-in* [11 {:a 22 :b (list 33 44)}] [1 :b 1] inc) ;; => [11 {:a 22, :b (33 45)}]
(update-in* {:a [11 #{22}]} [:a 1 22] #(* % 2)) ;; => {:a [11 #{44}]}
;; beyond end of sequence
(+ nil) ;; => nil
(update-in* [11 22 33] [3] +) ;; => (11 22 33 nil)
;; non-existent key-val
(not nil) ;; => true
(update-in* {:a {:b 11}} [:a :c] not) ;; => {:a {:b 11, :c true}}
;; updating a set member to an existing value
(update-in* #{11 12} [11] inc) ;; => #{12}
;; additional args
(update-in* [11 [22 [99]]] [1 1 0] #(/ %1 %2) 3) ;; => [11 [22 [33]]]