---
title: "Qim: Select from Your Immutable JavaScript Cake and Update It Symmetrically Too"
description: "An explanation of Qim: what it is and what it can do."
---

# Qim: Select from Your Immutable JavaScript Cake and Update It Symmetrically Too

An explanation of Qim: what it is and what it can do.

If you're reading this article, you're probably wondering a few things:

1. What the heck is Qim?
2. What the heck do I mean by "symmetrically"?
3. Is "JavaScript cake" a useful analogy or just an attempt to be clever?
4. How do I pronounce Qim?

So, let me try to answer those right now:

1. [Qim](https://github.com/jdeal/qim?utm_source=zapier.com&utm_medium=referral&utm_campaign=zapier) is a JavaScript library for handling immutable data with simple but powerful query paths. If you're thinking "oh god, not another immutable data library," hang on, I promise this one is at least different. If you're thinking "oh god, 'query' implies some weird DSL," I promise it's all just plain, composable JavaScript.
2. "Symmetrically" refers to the way that Qim can _update _a JavaScript object with the same query path used to _select from _a JavaScript object. This will make sense soon enough with some examples.
3. Sorry, nothing to do with cake here, but that was _a little _more than an attempt to be clever. Besides accentuating the second point, I'm referring to being able to query from deeply nested, plain JavaScript objects and arrays. Qim doesn't require wrapping your JS objects with any special wrapper. It doesn't require proxies or any other performance tax. You don't have to muck with prototypes to extend its usefulness. It doesn't stop you from using other libraries that expect plain JS data. It's just a simple functional library that allows you to do complex selections and updates on complex (or not-so-complex) data.
4. Qim is pronounced Kim. Because the [internet says so](https://www.howtopronounce.com/qim/). And me. The internet and me. It’s decided.

At Zapier, we love digging into these frontend topics. Sound like something you'd be into? We're currently hiring [frontend engineers](https://zapier.com/jobs/frontend-engineer/). We'd love to hear from you.

## Why Do We Need Another Javascript Library for Immutable Data?

If you're like our frontend and full-stack engineers at Zapier, you might have gone through a series of life-changing realizations that went something like this:

1. [React looks awesome](https://zapier.com/engineering/react-js-tutorial-guide-gotchas/). We have to try this.
2. React is awesome! Everything else is dead to us.
3. React is making us think functionally. Functional programming is pragmatic after all!
4. Hmm, mutating data is bad. If we use immutable data, things are much simpler. But making immutable changes to our data is kind of a PITA.
5. [Redux looks awesome](https://zapier.com/engineering/how-to-build-redux/). We have to try this.
6. Redux is awesome! And it enforces using immutable data. So we have to give up mutating data entirely. Okay, gotta find a better way to do immutable data.
7. Immutable.js looks awesome. We have to try this.
8. Ugh, using Immutable.js means giving up plain and simple JS data. That means lots of marshalling to interoperate with existing code or other libraries.
9. Okay, React has this [immutability helper](https://facebook.github.io/react/docs/update.html) which is pretty simple and works with plain JS. So let's use that.
10. We'd like to add some new commands to the immutability helper, so let's fork it.
11. Hmm, this immutability helper is getting kind of weird. And our code is a mix of one-off functions, this [other library](https://www.npmjs.com/package/sprout-data) used in a few places, and our immutability helper fork.
12. Some of our code for updating data is crazy complex. Relatively simple changes to nested data can become a computer science exercise.

You may have followed a different path, but if you hit bumps on the road to using immutable data and still find things harder than they should be, Qim might be able to help.

## Like Lodash

Okay, enough of the sales pitch, let's see some code. If you've used `get` or `set` from Lodash, a simple Qim example should be familiar.

[Try this example in a REPL.](https://jdeal.github.io/repl/#/?gist=5dbba9dffa63faa5a0eacdf34957b628/qim-find-set.js&script=https://unpkg.com/qim@0.0.49/umd/qim.js&stringify=object)

After this, `todoText` is `"invent time machine"`, and `todoState1` has its mistakes fixed:

Of course, `todoState0` is untouched because, well, otherwise, Qim wouldn't be a very good library for "handling immutable data."

At this point, `find` seems synonymous with `get` from Lodash, and `set` is synonymous with `set` from Lodash. But Qim is data-last and curried, and Qim never mutates the original data. So really, it's more synonymous with [Lodash/fp](https://github.com/lodash/lodash/wiki/FP-Guide?utm_source=zapier.com&utm_medium=referral&utm_campaign=zapier) or [Ramda](http://ramdajs.com/). Soon enough, though, the similarities will end.

## A Quick Aside on Currying

If you're unfamiliar with currying, here's a super-duper quick example to explain it:

[Try this example in a REPL.](https://jdeal.github.io/repl/#/?gist=5dbba9dffa63faa5a0eacdf34957b628/qim-currying.js&script=https://unpkg.com/qim@0.0.49/umd/qim.js&stringify=object)

If you leave off any of the parameters to Qim's functions, you get a function back and can later use that function on the data. That isn't a very useful example, but with more complex queries it can come in handy. (You'll see currying pop up in later examples.) A deeper explanation of currying is _way_ outside the scope of this article. Just google for "JavaScript currying," and you'll find plenty of good articles on the topic. (Or you might find some haters. It _can_ sometimes be confusing.)

## Not Like Lodash at All

So far, you're probably wondering what's the big deal here (besides my name, don't wear it out). That's the idea though: Simple things should be as unfancy as possible. I promise that things are about to get more interesting though.

Unlike Lodash's paths, Qim's queries can be made up of more than just strings. Qim's queries are actually made up of "navigators." (The "navigator" concept is borrowed from [Specter](https://github.com/nathanmarz/specter?utm_source=zapier.com&utm_medium=referral&utm_campaign=zapier), a Clojure library by [Nathan Marz](http://nathanmarz.com/).) Strings are just one simple type of navigator that let you query the value associated to a key from an object. Let's show off some other navigators now.

[Try this example in a REPL.](https://jdeal.github.io/repl/#/?gist=5dbba9dffa63faa5a0eacdf34957b628/qim-each.js&script=https://unpkg.com/qim@0.0.49/umd/qim.js&stringify=object)

After this, `todosText` is:

And `upperTodoState` is:

`find` and `set` are actually sugar over `select` and `update`, the core utilities of Qim. (It's a little more nuanced than that, but that's close enough without digging into the code.) `select` finds _all_ occurrences that match the given path. And `update` updates _all_ values that match the given path. `$each` is a navigator that acts like a wildcard. It matches all values of an object or array. `$apply` is a parameterized navigator that takes a function and transforms the current value.

Hopefully you're starting to see some of the power that Qim provides. Or, you're thinking "what up with those dollar signs?" Those are just a convention to differentiate declarative navigators from other functions. Navigators, even if they sometimes are functions, don't curry, because they don't ever do any work anyway. They just stand there waiting for navigations to occur.

Another difference from Lodash: Qim tries to avoid creating new objects if nothing actually changes.

After this, `upperTodoState` points to the same object as `todoState1`, because nothing actually changed.

## Symmetry

Notice that the select and update queries look similar. The only difference is that the update tacks on an extra `$apply` navigator to do a transformation. This is that promised symmetry. Let's do the same select and update using vanilla JavaScript and compare.

The equivalent select is pretty simple and expresses the problem pretty well. But the equivalent update gets _a lot_ more noisy and bears little resemblance to the select. We can illustrate Qim's symmetry even more using another navigator.

[Try this example in a REPL.](https://jdeal.github.io/repl/#/?gist=5dbba9dffa63faa5a0eacdf34957b628/qim-nav.js&script=https://unpkg.com/qim@0.0.49/umd/qim.js&stringify=object)

We're getting a little ahead of ourselves, but we can use `$nav` to create a custom navigator from other navigators. This way, we can compose queries from other queries. We'll talk more about that later. For now, it helps illustrate the symmetry of select and update.

Let's compare to Lodash/fp to see if we can make something less ugly than vanilla JS.

This is a lot better, but it still lacks Qim's symmetry. And, as our queries get more complex, Qim will stay expressive while Lodash/fp will get more complex, and updates will deviate even further from selects.

## Predicates

Here's where things get really interesting. Let's select the text of all the completed todos.

A function acts as a _predicate_ navigator. Used with `$each`, predicate functions act as filters on the values of objects or arrays. And of course, we get the same symmetry with updates. Let's change all completed todos to upper-case.

[Try this example in a REPL.](https://jdeal.github.io/repl/#/?gist=5dbba9dffa63faa5a0eacdf34957b628/qim-predicate.js&script=https://unpkg.com/qim@0.0.49/umd/qim.js&stringify=object)

This gives us:

Hopefully you see the pattern here. If you know how to _select_ data with Qim, you know how to _update_ with Qim. Let's compare again with Lodash/fp.

Here's where the whole "navigator" concept becomes obvious. With Lodash/fp, our select involved a filter, which was pretty obvious. But our update can't use a filter, because we'll filter out parts of the object that we want to keep. So our update is less declarative than our select. We have to break out of point-free style, and we have to add a condition to our `mapValues` function.

With Qim, our predicate navigator just selects parts of an object that will be passed along to the rest of the query. But the parts that aren't selected aren't removed. They stay intact. Immutable updates are just as obvious as immutable selects.

## Nested Queries

What if you want to update multiple things in the same query? You can use arrays to perform nested queries.

[Try this example in a REPL.](https://jdeal.github.io/repl/#/?gist=5dbba9dffa63faa5a0eacdf34957b628/qim-nested.js&script=https://unpkg.com/qim@0.0.49/umd/qim.js&stringify=object)

Now `todoState1` is:

Using an array as a navigator causes Qim to branch the query. When the array closes, you're back where you were.

We also introduced `$set` here. `$set` is like `$apply` but transforms the navigated value to a constant instead of applying a function.

You can nest `select` queries as well.

[Try this example in a REPL.](https://jdeal.github.io/repl/#/?gist=5dbba9dffa63faa5a0eacdf34957b628/qim-nested-select.js&script=https://unpkg.com/qim@0.0.49/umd/qim.js&stringify=object)

Since you typically want homogenous data for a select, that's a bit more unusual. Here, our `results` would be:

Put `$each`, predicates, and nested queries all together, and you can do complex updates with readable code. Let's add an `isArchived` flag to our completed todos and a due date to our incomplete todos.

[Try this example in a REPL.](https://jdeal.github.io/repl/#/?gist=5dbba9dffa63faa5a0eacdf34957b628/qim-each-predicate-nest.js&script=https://unpkg.com/qim@0.0.49/umd/qim.js&stringify=object)

Our `todoState2` is now:

## Other Useful Navigators

Qim has lots of other navigators. Let's touch on a few more.

### Removing a Property or Item ($none)

If you want to remove a property from an object or an item from an array, you can use the `$none` navigator. Let's remove all the completed todos.

[Try this example in a REPL.](https://jdeal.github.io/repl/#/?gist=5dbba9dffa63faa5a0eacdf34957b628/qim-none.js&script=https://unpkg.com/qim@0.0.49/umd/qim.js&stringify=object)

Now `cleanedTodos` is:

### Iterating Over Pairs ($eachPair)

If you want to use the key as well as the value when iterating over an object, you can use `$eachPair`. Let's select out some pairs just so it's obvious what pairs look like.

[Try this example in a REPL.](https://jdeal.github.io/repl/#/?gist=5dbba9dffa63faa5a0eacdf34957b628/qim-pairs.js&script=https://unpkg.com/qim@0.0.49/umd/qim.js&stringify=object)

Our pairs are just arrays with two items. The first item is the key, and the second item is the value.

That's similar to `toPairs` in Lodash, but now let's _update_ some pairs to add an `id` property to each todo.

[Try this example in a REPL.](https://jdeal.github.io/repl/#/?gist=5dbba9dffa63faa5a0eacdf34957b628/qim-update-pairs.js&script=https://unpkg.com/qim@0.0.49/umd/qim.js&stringify=object)

We get:

`$eachPair` converts each key and value to a pair (array of key and value), and we modify that pair as if it existed in the original data. We return a new pair, and `$eachPair` handles turning our new pairs back into an object. This is different from `toPairs` in Lodash where you have to apply the corresponding `fromPairs` to get back an object. Keep this in mind when we create custom navigators later.

### Prepending/Appending to an Array ($begin/$end)

You can append to an array with `$end` (and similarly prepend with `$begin`). Let's add a new todo.

[Try this example in a REPL.](https://jdeal.github.io/repl/#/?gist=5dbba9dffa63faa5a0eacdf34957b628/qim-end.js&script=https://unpkg.com/qim@0.0.49/umd/qim.js&stringify=object)

Our new state with the added todo:

Notice that `$end` selects an empty array from the end of the array, so you set it to a non-empty array to append any number of items.

### All the Other Navigators

There are lots of other basic navigators. I'll spare you from more boring examples, but here's a quick summary of a few more:

`$first` and `$last`, unsurprisingly, navigate to the first and last items of an object or array.

`$slice` navigates to a slice of an array, and similarly, `$pick` navigates to a subset of an object.

You can check out these and all the other basic navigators in the [Qim README](https://github.com/jdeal/qim#named-navigators). For now, buckle up, because we're going to start looking at some of Qim's more advanced navigators.

## Custom Navigators

Because queries are just lists of navigators, it's pretty straightforward to extend Qim. Just add new navigators. And it's pretty straightforward to create new navigators: Just build on top of existing _parameterized_ navigators. As a simple example, let's add a `$true` and a `$false` navigator.

Now we can use those just like any other navigators.

[Try this example in a REPL.](https://jdeal.github.io/repl/#/?gist=5dbba9dffa63faa5a0eacdf34957b628/qim-simple-custom-navigators.js&script=https://unpkg.com/qim@0.0.49/umd/qim.js&stringify=object)

To make parameterized navigators, just create functions that return navigators. We learned about `$nav` a little earlier. Let's use that to create a parameterized navigator.

[Try this example in a REPL.](https://jdeal.github.io/repl/#/?gist=5dbba9dffa63faa5a0eacdf34957b628/qim-simple-parameterized-navigators.js&script=https://unpkg.com/qim@0.0.49/umd/qim.js&stringify=object)

That's a little contrived, but you get the idea. You can compose queries pretty easily by building on top of `$nav`.

## Crazier Custom Navigators Using $nav

Now, let's reveal some of `$nav`'s secret sauce and start making _really_ custom navigators. Remember [this example](https://zapier.com/engineering/qim-immutable-javascript/#update_pairs) where we used `$eachPair` to add an `id` (from the key) to each of our todos?

If we could get `id` into scope somehow, we could just use a path like `['id', $set(id)]` to set the `id` property instead of using an `$apply` and reverting back to vanilla JS. To clarify what we want, let's first set the `id` property for each todo to a static value.

The `1` navigates to the second item in the pair which is the todo itself. Then we navigate to the `id` property and set that to a static value. That gives us:

But we don't want to set `id` to `'foo'`. We want to set it to a dynamic value based on first item in the pair, which is the key of the todo. But how do we get a dynamic value into our `$set`? We can do that with `$nav`.

[Try this example in a REPL.](https://jdeal.github.io/repl/#/?gist=5dbba9dffa63faa5a0eacdf34957b628/qim-dynamic-nav.js&script=https://unpkg.com/qim@0.0.49/umd/qim.js&stringify=object)

`$nav` takes a function that receives the current navigated object as the first parameter, and it can return a dynamic query path to continue navigating. In this example, we navigate to each pair, and:

1. Put the `id `into scope. The function passed to `$nav `gets the array pair as the first parameter, so we use array destructuring to pull off the key part of the pair and bind it to a local variable `id`.
2. Dynamically navigate to the value part of the array pair with `1`, just like we did with our static navigation.
3. Navigate to the `id `property which doesn't exist but will be created. By default, if a key doesn't exist, and you continue navigating, `qim `assumes that you want to create an object. (You can use [$default](https://github.com/jdeal/qim#defaultvalue) if you want to change that behavior.)
4. Use `$set `to set the value of `id `to the key part of the pair. After that, we have:

Now that we can dynamically choose a path, we can go completely mad scientist. Hold onto your butts…

First, assume we have a tree of todos like this:

Now, let's cook up this navigator.

Recursive queries! Fun! We also see another trick up `$nav`'s sleeve. It can take multiple paths, and it will navigate along each one. If you're confused about the difference between this and nested queries (arrays), just remember that nested queries don't really affect the path followed by the rest of the query. So in a query like this:

`['a', ['b'], ['c'], 'd']`

We'll navigate along the paths `['a', 'b']`, `['a', 'c']`, and `['a', 'd']`. We could remove the subqueries, and we would still navigate to `['a', 'd']`. With a query like this:

`['a', $nav(['b'], ['c']), 'd']`

We'll navigate to `['a', 'b', 'd']` and `['a', 'c', 'd']`.

With that tangent out of the way, let's use our fancy navigator!

[Try this example in a REPL.](https://jdeal.github.io/repl/#/?gist=5dbba9dffa63faa5a0eacdf34957b628/qim-recursive-nav-select.js&script=https://unpkg.com/qim@0.0.49/umd/qim.js&stringify=object)

This fine piece of recursive work gives us:

And of course we can use it for an update too:

[Try this example in a REPL.](https://jdeal.github.io/repl/#/?gist=5dbba9dffa63faa5a0eacdf34957b628/qim-recursive-nav-update.js&script=https://unpkg.com/qim@0.0.49/umd/qim.js&stringify=object)

And we end up with:

Just like I promised, the symmetry has followed us all the way to crazy complex queries. Recursive queries like this are not often necessary, but it's nice to know that Qim has the power when you need it. And it's nice to know that you can hide all that complexity behind a single navigator like `$walkTodos`.

## Less Crazy Custom Navigators Using $lens

Hopefully you survived the _recursive_ descent into madness in the last section. "Lens" might sound like we're going deeper, but it's actually pretty simple. It's really just a two-way version of `$apply`. In fact, Qim could have just added another parameter to `$apply`, but that fancy "lens" word was just too tempting.

As an example, let's say we want to create a navigator that treats our todo state as an array of todos. We could easily map to an array with `$apply`, but we need a way to convert from an array back to our state object.

The first function passed to `$lens` will get the current value, just like an `$apply`. In our example, we map over the `ids` to convert the state into an array. The transformed value will be passed to the rest of the query.

For an update, that transformed value will be passed into the second function, along with the original value. In our example, we reduce that array back to an object. Once we've created the navigator, we can use it like any other.

[Try this example in a REPL.](https://jdeal.github.io/repl/#/?gist=5dbba9dffa63faa5a0eacdf34957b628/qim-custom-lens.js&script=https://unpkg.com/qim@0.0.49/umd/qim.js&stringify=object)

We're able to change our `id` property in one place, and that change is reflected back to both places in the state.

Kind of cool, huh? Again, you probably don't want to do a radical transformation like this every day, but it's nice to know the option is there.

## Lower-Level Custom Navigators with $traverse

The last stop on the custom navigator train is `$traverse`. If you want to make efficient navigators or navigators that iterate like `$each`, then `$traverse` is going to give you the most control.

Let's implement a completely contrived `$eachTodo` iterator that works like a combination of our `$todosArray` above and `$each`.

Now we can do this:

[Try this example in a REPL.](https://jdeal.github.io/repl/#/?gist=5dbba9dffa63faa5a0eacdf34957b628/qim-custom-traverse.js&script=https://unpkg.com/qim@0.0.49/umd/qim.js&stringify=object)

Using `$traverse`, you have complete control over whether the rest of the query gets called (and how many times it gets called) via the `next` function. This is different from `$lens` where `next` is implicitly called once and only once. Also, unlike `$lens` where the select function feeds into the update function, for `$traverse`, the two halves can have completely independent implementations.

Of course, if you've been paying attention, you might be thinking we could have just done this:

And you'd be right! But you may have also noticed that imperative for loop and that crazy `isReduced` utility. If we use our custom `$traverse` implementation of `$eachTodo`, then this is efficient:

`**const**`` firstTodo = find([$eachTodo], todoState1);`

It will only create and send the first todo item through `next`. `find` will wrap that one in a "reduced" wrapper, and we'll break out of our for loop because of the `isReduced` check. With the `$nav` version, we always create the full array even if we don't use all of it.

I recommend only reaching for this level of control if you really need it. The higher-level `$nav` and `$lens` are typically going to perform well enough for most jobs. Also, I'd like to start experimenting with using lazy sequences and see if I can make the `$nav` version just as efficient as the custom `$eachTodo` navigator.

## Context

If you're still with me, let's turn things up to 11. Or if you thought they were already at 11, I guess, uh, 12?

Any time you hear about something called "context," it's usually a feature that you're supposed to use sparingly. It's the same with Qim. This feature is usually something to avoid, but it's essential for some cases.

Just to illustrate, let's use context where it's _not_ necessary. Let's tweak the text of all of our todos, tacking on the `id`.

[Try this example in a REPL.](https://jdeal.github.io/repl/#/?gist=5dbba9dffa63faa5a0eacdf34957b628/qim-set-context-apply.js&script=https://unpkg.com/qim@0.0.49/umd/qim.js&stringify=object)

We use `$setContext` to save a value to the context. Don't be thrown by the `find(0)`. That's just making use of currying. We could have written it like this:

`$setContext('id', pair => pair[0])`

`$setContext` takes a key that determines where to store the context and a function which takes the current value and returns the value to store at that key. The context only applies to that particular path. So for each pair, we store a different `id` value in the context.

Later, we pull the value back out of context in our `$apply` function. Up till now, it's been a secret, but the second parameter to our `$apply` function is the context.

The result of this is:

`$nav` also has that same second context parameter. So you can create navigators that pass along data to each other.

[Try this example in a REPL.](https://jdeal.github.io/repl/#/?gist=5dbba9dffa63faa5a0eacdf34957b628/qim-set-context-nav.js&script=https://unpkg.com/qim@0.0.49/umd/qim.js&stringify=object)

After this rather contrived example, `firstTodoText` is `"invent time machine"`. You should _not_ use context for simple cases like this. Instead, just use a local variable, or use `$nav` to pull something into scope. But, again, the power is there if you need it.

Okay, ready for full crazy? Remember that tree of todos? And that `$walkTodos` navigator? Let's tweak that a bit:

Here we're using `$pushContext` (which is really just a variant of `$setContext`) that appends the value to an array. That way, you can grab all the collected values in your `$apply` function. And this is good for, you guessed it: Recursion!

Let's select out a flattened list of all of the todo text along with a path string.

[Try this example in a REPL.](https://jdeal.github.io/repl/#/?gist=5dbba9dffa63faa5a0eacdf34957b628/qim-push-context.js&script=https://unpkg.com/qim@0.0.49/umd/qim.js&stringify=object)

And we get:

Okay, if you've had enough crazy, that's it. There's no more code. Just words.

## Performance

So how fast is Qim? Well, the primary goal of Qim is to be expressive, but actually, it's pretty fast. If you're currently using Lodash/fp, Qim is going to be quite a bit faster. If you're using Ramda, Qim will probably be faster in some areas, slower in others, but overall about equivalent. For Immutable.js, it all depends on how much you're pushing Immutable.js and how much marshalling you're doing. For huge lists, Immutable.js is great. If you do lots of marshalling back and forth to vanilla JS objects though, well, that's a lot of why Qim exists. It lets you use plain JS objects and keeps things _performant enough_.

There are [some benchmarks](https://github.com/jdeal/qim/blob/master/docs/benchmark-results.md?utm_source=zapier.com&utm_medium=referral&utm_campaign=zapier) in the Qim repo. As with all micro-benchmarks, take all this with a grain of salt though and try it for yourself!

## Stability

Should you use Qim?

Qim is new and might change! There are lots of crazy ideas here, and it's likely I've made a few mistakes. Having said that, we're using it in production at Zapier now, so I won't be making any radical changes without thinking them through carefully! For now, I'd probably recommend pinning to a specific version to be safe, though.

## Thanks

If you made it this far, thanks for reading about Qim! You survived my simple, contrived, and sometimes crazy examples. The idea was to show you how Qim can scale from tiny problems all the way to complex ones. Hopefully you've seen enough to give it a try!

If so, you can keep hacking on the examples with the REPL links throughout this article, or you can work on [a blank slate](https://jdeal.github.io/repl/#/?gist=5dbba9dffa63faa5a0eacdf34957b628/qim-push-context.js&script=https://unpkg.com/qim@0.0.49/umd/qim.js&stringify=object) that has all of Qim's functions ready to use. If you want to dig in a little more, [install Qim](https://github.com/jdeal/qim#installation) or clone [the repo](https://github.com/jdeal/qim?utm_source=zapier.com&utm_medium=referral&utm_campaign=zapier). All the examples in this article are [in the Qim repo](https://github.com/jdeal/qim/blob/master/test/zapier-blog-post-examples.js?utm_source=zapier.com&utm_medium=referral&utm_campaign=zapier) as unit tests.

Have fun!