Clojure - using specter to transform a nested data structure, replacing one node with several

Related searches

I'm using specter to transform nested data structures in Clojure, but I haven't got the hang of it yet. In particular, I'm trying to create a transformation that will find an item - at any depth - that matches a predicate, and replace it will several items.

[:top
 [:arbitrary 1 2
  [:nesting
   2
   3
   [:needle] ; <-- the thing to find
   ]]]

-->

[:top
 [:arbitrary 1 2
  [:nesting
   2
   3
   [:n1] [:n2] [:n3]  ; <-- 3 items inserted in the place of 1
   ]]]

What I can't figure out is how to splice the replacement items into the parent vector, i.e., how to replace one item with three items, and not with one item containing three children.

I don't know how to do this using Specter, but here's a function to do it with clojure.zip:

(defn splice-replace [zipper smap]
  (loop [loc zipper]
    (if (z/end? loc)
      (z/root loc)
      (recur
       (z/next
        (if-let [sub (smap (z/node loc))]
          (reduce (comp z/right z/insert-right)
                  (z/replace loc (first sub))
                  (rest sub))
          loc))))))

You can call it with a zipper of your data structure and a map from values you want to replace to a sequence of their replacement values to be spliced into their position:

(def zipper
  (z/vector-zip [:top
                 [:arbitrary 1 2
                  [:nesting 2 3 [:needle]]]]))

(splice-replace zipper {[:needle] [[:n1] [:n2] [:n3]]})
 => [:top [:arbitrary 1 2 [:nesting 2 3 [:n1] [:n2] [:n3]]]]

(splice-replace zipper {[:nesting 2 3 [:needle]] (range 3 10)})
=> [:top [:arbitrary 1 2 3 4 5 6 7 8 9]]

redplanetlabs/specter: Clojure(Script)'s missing piece, Contribute to redplanetlabs/specter development by creating an account on GitHub. GitHub is home to over 50 million developers working together to host and review Specter rejects Clojure's restrictive approach to immutable data structure excels at querying and transforming nested and recursive data, important use� Specter . Specter rejects Clojure's restrictive approach to immutable data structure manipulation, instead exposing an elegant API to allow any sort of manipulation imaginable. Specter especially excels at querying and transforming nested and recursive data, important use cases that are very complex to handle with vanilla Clojure.

(defn replace-needle [input replacement]
    (let [needle-parent?     #(= % [:needle])
          NEEDLE-PARENT      (recursive-path
                                 [] p (cond-path
                                          #(and (vector? %) (some needle-parent? %)) [(continue-then-stay [ALL p])]
                                          vector? [ALL p]))
          inject-replacement (fn inject [x] (vec (mapcat #(if (needle-parent? %) replacement [%]) x)))]
        (transform [NEEDLE-PARENT] inject-replacement input)))


(let [input       [:top
                   [:arbitrary 1 2
                    [:nesting 2 3 [:needle]]]]
      replacement [[:n1] [:n2] [:n3]]]
    (replace-needle input replacement))

Clojure's missing piece - thoughts from the red planet, In this post I'll explain why I call Specter "Clojure's missing piece" a few examples where Clojure does immutable programming well: What's also needed is an elegant way to compose them so that a nested data structure is That subsequence is then replaced by whatever sequence it's transformed to. 6 Clojure - using specter to transform a nested data structure, replacing one node with several Mar 14 '19 4 Scripting Eclipse with Rhino: classloader belongs to the plugin providing Rhino, not the plugin using it Feb 8 '10

I thought it should be possible to find a vector that contains [:needle], and then the index of the [:needle], and then use srange to splice the new elements into the parent at that index, but I didn't find a way to do it using Specter.

Here's that same idea expressed using clojure.walk:

(require '[clojure.walk :refer [postwalk]])

(postwalk (fn [node]
            (if (and (vector? node)
                     (some (partial = [:needle]) node))
              (let [idx (.indexOf node [:needle])]
                (vec (concat (take idx node)
                             [[:n1] [:n2] [:n3]]
                             (drop (inc idx) node))))
              node))
          data)

;; => [:top [:arbitrary 1 2 [:nesting 2 3 [:n1] [:n2] [:n3]]]]

Functional-navigational programming in Clojure(Script) with Specter , One of Clojure's greatest strengths is its powerful facilities for doing This data structure contains information about a bank and its list of customers. Now suppose you want to do a simple transformation that transfers money from a Here are five different ways to increment the value in that nested map:� Clojure Project Structure. Finally let’s talk about a typical project structure for a Clojure project. Since Clojure code runs on Java virtual machine, most of the project structure within Clojure is similar to what you would find in a java project. Following is the snapshot of a sample project structure in Eclipse for a Clojure project.

specter 2017-04-24, I should also mention I did try using data.int-map last night and the More realistically for calculating gradients I'd need to use my linear-transform function in this yet instead of between two different maps, to values store in adjacent like merge a map in one location in a data structure with another map� Sequential destructuring represents a sequential data structure as a Clojure vector within a let binding. This type of destructuring can be used on any kind of data structure that can be traversed in linear time, including lists, vectors, and anything that can produce a sequence.

[PDF] Working with tree data structures, clojure.walk/postwalk “visits” the node after recursively walking the children ( depth and clojure.walk/postwalk-‐replace walk a nested structure and replace Navigates nested data structures using Specter's walker. (transform. [(walker number?) even?] #(* -1 %). [1 [[[2]] 3] 5 [8 [7 6]] 2]) And a few other bits and pieces:. Learn how to elegantly manipulate nested data structures with Specter. Specter: https://github.com/nathanmarz/specter Language: http://clojure.org Editor: ht

nil is a possible value of any data type in Clojure. nil has the same value as Java null. The Clojure conditional system is based around nil and false, with nil and false representing the values of logical falsity in conditional tests - anything else is logical truth.

Comments
  • Although it's not specter this does what I need
  • Thanks. I was trying to create a path that would match the item itself, and then try and control how the replacement(s) were inserted in the parent, whereas you are searching for the item's parent and transforming that, which is easier to understand. The solution doesn't always replace all occurrences: (let [input [:top [:needle] [:a [:needle]]] replacement [1 2 3]] (replace-needle input replacement)) ;-> [:top 1 2 3 [:a [:needle]]]
  • Another approach would be finding a vector that contains [:needle], and then the index of the [:needle], and then use srange to splice the new elements into the parent at that index. Is it possible to express "finding a vector that contains [:needle], and then the index of the [:needle]" in Specter without the use of custom navigators?
  • I made it recursive.
  • It's an interesting idea. My aim is to take a simple structure and progressively transform it into ooxml (for a Word document). In a previous life I'd probably have used XSLT and maybe there are better solutions than specter, but it looks too interesting not to try and learn it.
  • Specter should be usable. Nathan's talks are great. I've dabbled in it. My experience so far: for the usual cases the concise syntax is not worth the unfamiliarity for other developers. And for advanced cases I find it quite unreadable with things like custom navigators, multi-paths and terminals. In its defense, at my previous job we had a problem for which I wrote a unit test that seemed unsolvable in normal Clojure. My colleague got to work and in one day produced 10 lines of Specter code that solved it. A work of art.
  • Maybe with Specter you can create a custom TreeWalker like in the example from the README. :)