Specter Clojure Library

repository·master·Indexed 25 days ago

https://github.com/redplanetlabs/specter

A high-performance Clojure library for querying and transforming complex, nested, and recursive data structures using a composable 'navigator' API. It provides core functions like select, transform, and setval to precisely target data for retrieval or modification while preserving original collection types. Key features include inline caching for performance, navigators such as ALL, END, and MAP-VALS, and support for polymorphic data navigation via protocolpaths. Compatible with ClojureScript.

Tokens
1.7K
Snippets
10
Records
14
Agent score
33%

What's inside Specter

  1. Overview of Specter data manipulation

    master

    Specter is a Clojure library designed for powerful and efficient querying and transformation of nested and recursive data structures. It replaces Clojure's restrictive approach with a 'navigator' abstraction. By composing navigators into a 'path', you can precisely target specific parts of a data structure for retrieval or modification.

    Key features include:

    • High Performance: Uses advanced dynamic techniques (inline caching) to achieve performance that rivals hand-optimized code, often outperforming Clojure's built-in get-in and update-in.
    • Precise Transformations: Transforms target specific parts of a structure while leaving the rest unchanged and preserving the original collection type (e.g., a sorted map remains a sorted map).
    • Expanded Capabilities: Provides navigators for operations not natively supported by Clojure, such as inserting into the middle of a sequence or prepending to a vector.
  2. Use Specter for data transformation and selection

    master

    Specter provides powerful tools for navigating, selecting, and transforming complex data structures using paths.

    Core Functions

    • select: Retrieves values from a data structure based on a path.
    • transform: Applies a function to values found at a specific path.
    • setval: Sets values at a specific path, allowing for replacement or removal.

    Common Path Selectors

    • ALL: Matches all elements in a collection.
    • END: Matches the last element of a collection.
    • MAP-VALS: Matches all values in a map.
    • srange start end: Matches a range of indices (inclusive of start, exclusive of end).
    • walker: Recursively traverses a data structure.
    • filterer: Applies a predicate to filter elements within a path.
    • compact: A specialized selector used with setval to remove empty structures during navigation.
  3. Run individual benchmarks

    master

    Run specific benchmarks by passing their names as command line arguments to the scripts/run-benchmarks script. Note that benchmark names containing spaces must be enclosed in quotes. The order of arguments does not matter.

    scripts/run-benchmarks "prepend to a vector" "filter a sequence" 
  4. Use Specter in ClojureScript

    master

    Specter is compatible with ClojureScript. However, since ClojureScript does not support (use ...) or :refer in the same way as Clojure, you must use :as and explicitly include required macros in your namespace declaration.

    ;; Recommended way to import in ClojureScript
    (:require [com.rpl.specter :as s])
    (:require [com.rpl.specter :as s :refer-macros [select transform]])
  5. Configure clj-kondo for Specter

    master

    Because Specter uses macros to define many of its internal vars, clj-kondo may report them as unresolved. To fix this, add the following configuration to your .clj-kondo/config.edn file to lint them as standard Clojure definitions.

    {:lint-as {com.rpl.specter/defcollector clojure.core/defn
               com.rpl.specter/defdynamicnav clojure.core/defn
               com.rpl.specter/defmacroalias clojure.core/def
               com.rpl.specter/defnav clojure.core/defn
               com.rpl.specter/defrichnav clojure.core/defn}}
  6. Set values at a specific path with `setval`

    master

    Use setval to replace a value at a precise location within a data structure using a path of navigators.

    Example: Append a sequence of elements to the end of a nested vector.

    (def data {:a [1 2 3]})
    
    ;; Specter
    (setval [:a END] [4 5] data)
  7. Transform values using filters and position navigators

    master

    You can combine filterer navigators with position-based navigators (like LAST) to target specific elements based on predicates.

    Example: Increment the last odd number in a sequence.

    (def data [1 2 3 4 5 6 7 8])
    
    ;; Specter
    (transform [(filterer odd?) LAST] inc data)
  8. Map functions over collections with `ALL`

    master

    The ALL navigator targets every value within a collection. Unlike standard Clojure map, using transform with ALL preserves the original collection type and order without converting to a lazy sequence.

    ;; Specter
    (transform ALL inc data) ;; works for all Clojure datatypes with near-optimal efficiency
  9. Transform nested data with `transform`

    master

    Use transform to apply a function to specific parts of a data structure identified by a path of composed navigators. This is more concise and often faster than manual Clojure recursion.

    Example: Increment every even number nested within a map of vectors of maps.

    (def data {:a [{:aa 1 :bb 2}
                   {:cc 3}]
               :b [{:dd 4}]})
    
    ;; Specter
    (transform [MAP-VALS ALL MAP-VALS even?] inc data)