• Stars
    star
    170
  • Rank 218,432 (Top 5 %)
  • Language
    Clojure
  • License
    MIT License
  • Created almost 10 years ago
  • Updated almost 9 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Simpler and more powerful assoc/dissoc/update-in for both Clojure and ClojureScript

Instar

Build Status Examples tested with midje-readme Dependency Status

Instar |ˈɪnstɑː|

noun Zoology

A phase between two periods of moulting in the development of an insect larva or other invertebrate animal.

ORIGIN late 19th cent.: from Latin, literally ‘form, likeness’.

Instar is a library to unify assoc, dissoc and update-in into a coherent and easy to use whole, while also adding wildcard matching on paths. This creates a simple and powerful function for all transformations. There are also functions to extract data based on the same path structure.

Examples

Nested paths unifies assoc-in, update-in and (the still not in the standard lib) dissoc-in:

(def m {:foo {:bar {:baz 1}}})

; Traditional:
(assoc-in  {} [:foo :bar :baz] 7)      => {:foo {:bar {:baz 7}}}
(update-in m  [:foo :bar :baz] inc)    => {:foo {:bar {:baz 2}}}
(update-in m  [:foo :bar] dissoc :baz) => {:foo {:bar {}}}

; With instar:
(transform {} [:foo :bar :baz] 7)      => {:foo {:bar {:baz 7}}}
(transform m  [:foo :bar :baz] inc)    => {:foo {:bar {:baz 2}}}
(transform m  [:foo :bar :baz] dissoc) => {:foo {:bar {}}}

Wildcards makes updating multiple values easy:

(transform {:foo {:bar {:baz 1, :qux 4}
                  :bar2 {:baz 2, :qux 5}}}
           [:foo * *] inc)
=>
{:foo {:bar {:baz 2, :qux 5},
       :bar2 {:baz 3, :qux 6}}}

Besides the flamboyant match-all asterisk, regular expressions can be used for more focused matches:

(transform {:foo {:bar {:baz 1, :qux 4}
                  :zip {:baz 2, :qux 5}}}
           [:foo #"^ba" #"^ba"] inc)
=>
{:foo {:bar {:baz 2, :qux 4},
       :zip {:baz 2, :qux 5}}}

Key is neither string nor keyword?

Require a more delicate touch?

Clojure functions are treated as match predicates:

(transform {:vector [0 1 2 3 4 5 6]}
            [:vector odd?] inc)
=>
{:vector [0 2 2 4 4 6 6]}

(transform {:map {"a" 1, "ab" 2, "abc" 3}}
           [:map (comp even? count)] inc)
=>
{:map {"a" 1, "ab", 3, "abc", 3}}

And the coup de grâce, the combination of all of the above:

(transform {:foo {:bar {:baz 1, :qux 4, :quux 7}}}
           [:foo * *] inc
           [:foo keyword? #"qu+x"] inc
           [:foobar] "hello"
           [:foo :bar :baz] dissoc)
=>
{:foo {:bar {:qux 6, :quux 9}}, :foobar "hello"}

You can also use instar for getting deep values, either with pairs of [path value] or just the values:

(get-in-paths {:foo {:bar {:baz 1, :qux 4, :quux 7}}}
              [:foo * *])
=>
[[[:foo :bar :baz] 1]
 [[:foo :bar :qux] 4]
 [[:foo :bar :quux] 7]]


(get-values-in-paths {:foo {:bar {:baz 1, :qux 4, :quux 7}}}
                     [:foo * *])
=>
[1 4 7]

Capture groups

Capture can be performed using the functions %>and %%, which will replace the regular argument to any transforming functions (the data being transformed) with the value captured at that point in the path. Note that multiple captures can be used, which will then form additional arguments to the function.

The two capture types differ around whether their enclosed path segment becomes part of the transformation path. The non-resolving form is useful for capturing values from siblings outside of the fully resolved path.

Use %% for non-resolving capture, and %> for the resolving variant, as demonstrated below:

(transform {:users [{:name "Dan", :age 23}
                    {:name "Sam", :gender :female}]}
           [:users (%> *) :keys] keys)
=>
{:users [{:name "Dan", :age 23, :keys '(:name :age)}
          {:name "Sam", :gender :female, :keys '(:name :gender)}]}


(:users
  (transform {:users [{:name "Dan", :age 23}
                      {:name "Sam", :gender :female}]
              :aliases {"Dan" ["Dante" "Daniel"]
                        "Sam" ["Samantha", "Samoth"]}}
    [(%% :aliases) :users (%> *) (%% :name)]
      (fn [aliases user name] (assoc user :aliases (aliases name)))))
=>
[{:name "Dan", :age 23,         :aliases ["Dante" "Daniel"]}
 {:name "Sam", :gender :female, :aliases ["Samantha", "Samoth"]}]

Note also that the non-resolving capture can conveniently support multiple segments:

(transform {:a {:b {:c 42}}}
           [(%% :a :b :c) :d :e :f] vector)
=>
{:a {:b {:c 42}}, :d {:e {:f [42]}}}

Installation

Add the following dependency to your project.clj file:

Clojars Project

Longer example

This example is based on an actual use case for atpshowbot

Say we have the following data structure:

(def big-map
  {:votes {"title1" {:voters #{"74.125.232.96" "74.125.232.95"}
                     :author "nick1"
                     :author-ip "74.125.232.96"}
           "title2" {:voters #{"74.125.232.96" "74.125.232.95"}
                     :author "nick2"
                     :author-ip "74.125.232.96"}}
   :links [["link" "nick1" "74.125.232.96"]
           ["another link" "nick2" "74.125.232.96"]]})

We'd like to send this to a browser, but it's pretty bad to send the users IP addresses. We still want to know how many have voted though and if the user has already voted. This is the transformation to do that:

(transform big-map
  [:votes (%> *) :votes] #(count (:voters %))
  [:votes (%> *) :did-vote] #(contains? (:voters %) "74.125.232.96")
  [:votes * :voters] dissoc
  [:votes * :author-ip] dissoc
  [:links] #(for [[x y z] %] [x y]))

=>

{:votes {"title1" {:did-vote true,
                   :votes 2,
                   :author "nick1"},
         "title2" {:did-vote true,
                   :votes 2,
                   :author "nick2"}},
 :links [["link" "nick1"]
         ["another link" "nick2"]]}

More Repositories

1

mutmut

Mutation testing system
Python
868
star
2

django-fastdev

An app to make it safer, faster and more fun to develop in Django
Python
136
star
3

hammett

Fast python test runner, compatible with a subset of pytest
Python
73
star
4

CMi

Replacement for XBMC with Django+VLC+WebKit
Python
36
star
5

elm-cog

Code generation for Elm, using Ned Batchelder's Cog
Python
32
star
6

indent-clj

A little experiment into how Clojure with inferred paren might look like
Clojure
31
star
7

iso8601

Python library to parse dates in the formats in the ISO 8601 standard.
Python
26
star
8

urd

A scheduler for Django projects
Python
18
star
9

pytest-readme

Make sure your README doesn't contain broken code
Python
13
star
10

DevBar

Take control over your workflow by putting it in your macOS menu bar
Swift
10
star
11

midje-readme

A Leiningen plugin to pull tests from your README.md into midje.
Clojure
10
star
12

okrand

Okrand is an internationalization/translation tool for Django
Python
9
star
13

clojure-validate-indent

Check that the indents in your code align with the parenthesis
Clojure
9
star
14

pytest-test-this

Plugin for pytest to run relevant tests, by naively checking if a test contains a reference to the symbol you supply
Python
7
star
15

test-benchmarks

Python
6
star
16

Waker

Waker is the alarm program that knows what you're up to and sets the time intelligently, based on your calendar.
Objective-C
6
star
17

atpshowbot

An irc bot for the Accidental Tech Podcast
Clojure
6
star
18

Frej

Swift
5
star
19

relatedhow

A website to quickly find out how species are related
Python
4
star
20

git-stats2

Statistics for git repos
Python
4
star
21

PythonMate

Debugger GUI interface for Python and TextMate
Python
4
star
22

cheap-ruler-swift

Swift port of MapBox's cheap-ruler, between 13% and 10471% faster
Swift
3
star
23

defk

Compact keyword arguments for clojure
Clojure
3
star
24

BoxedDock

A fast app switcher for macOS
Swift
3
star
25

mammon

Get control over your finances
Python
3
star
26

LineOps-intellij-plugin

Line operations based on bookmarks
Java
2
star
27

p

Python
2
star
28

Misc

Random small pieces of code that don't justify their own repositories
Python
2
star
29

readable_lisp

Readable Lisp
Python
2
star
30

forum

Reimplementation of the best ideas of SKForum in python
Python
2
star
31

Knitting

Parsing and manipulating knitting instructions
Python
2
star
32

instar-py

Simpler transformations for pyrsistent data structures
Python
1
star
33

WCS-Hub

Implementation of World Swing Dance Council event code
Python
1
star
34

analytics

Super simple web analytics app
Python
1
star
35

sentry-ratelimit

Plugin to sentry for rate limiting low volume errors you don't care about
Python
1
star
36

python-terminal-menu

Simple little lib for terminal menus
Python
1
star
37

Supernaut

iommi presentation
Python
1
star
38

pannkakor

Den bästa sidan för pannkaksrecept
JavaScript
1
star
39

scientist

Python
1
star
40

curia

Misc django code
Python
1
star
41

RandomPlaceInCode

Jump to a random place in your codebase
Python
1
star
42

cheatsheet-reagent

Reagent version of the Clojure cheatsheet
Clojure
1
star
43

BitPhone

My app Bit (available in the iOS app store). Old and horrible code :P
Objective-C++
1
star
44

CursorHighlight

OS X: Highlight the cursor with a white ring.
Objective-C
1
star
45

ivrit

Generate type stubs for your project based on name->type mapping configuration
Python
1
star