redplanetlabs / specter

Clojure(Script)'s missing piece
Apache License 2.0
2.52k stars 105 forks source link

Consider adding navigators that perform conditioned removals #240

Closed nathanmarz closed 6 years ago

nathanmarz commented 6 years ago

These could be called "ensure" operations, e.g.:

(def data [{:a [{:b 10}]} {:a [{:q 20} {:z 42}]}])

(defdynamicnav ensure-matching [path]
  (if-path path
    (multi-path path (if-path path STOP (terminal-val NONE)))
    (terminal-val NONE)
    ))

(defn ^:direct-nav ensure-pred [pred-fn]
  (path (stay-then-continue (if-path (pred pred-fn) STOP (terminal-val NONE)))))

(transform
  [ALL
   (ensure-matching (must :a))
   (ensure-pred #(-> % empty? not))
   ALL
   (ensure-matching (must :b))
   ]
  str
  data)
nathanmarz commented 6 years ago

Here's an even simpler approach:

(defdynamicnav ensure-matching* [path]
  (multi-path path (if-path path STOP (terminal-val NONE))))

(def ensure-matching (eachnav ensure-matching*))

(transform
  [ALL
   (ensure-matching (must :a) ALL (must :b))
   ]
  str
  data)
nathanmarz commented 6 years ago

One common use case that should be addressed here is removal of a subvalue if it is an empty collection.

nathanmarz commented 6 years ago

Here's a potential solution for removing when empty:

(let [ensure* (stay-then-continue (if-path empty? (terminal-val NONE)))]
  (defdynamicnav ensure [& path]
    (interleave (repeat (count path) ensure*) path)
    ))

(setval [:a (ensure :b :c)] NONE {:a {:b {:c 1}}})
;; => {}

(setval [:a (ensure :b :c)] NONE {:a {:b {:c 1} :d 2}})
;; => {:a {:d 2}}
nathanmarz commented 6 years ago

Need a different name than ensure since Clojure already has a function named ensure.

nathanmarz commented 6 years ago

Name ideas: compact, trim, tighten

nathanmarz commented 6 years ago

To make these navigators work for both select and transform, need to turn terminal and vterminal into no-ops for select* instead of throwing exceptions as they do now.

nathanmarz commented 6 years ago

compact is implemented in master. Other kinds of related navigators are too application-specific to add to Specter, whereas removing empty subvalues is a common, generic use case.