README.markdown 4.98 KB
Newer Older
Patiphan Marak's avatar
Patiphan Marak committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
traverse
========

Traverse and transform objects by visiting every node on a recursive walk.

examples
========

transform negative numbers in-place
-----------------------------------

negative.js

````javascript
var traverse = require('traverse');
var obj = [ 5, 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];

traverse(obj).forEach(function (x) {
    if (x < 0) this.update(x + 128);
});

console.dir(obj);
````

Output:

    [ 5, 6, 125, [ 7, 8, 126, 1 ], { f: 10, g: 115 } ]

collect leaf nodes
------------------

leaves.js

````javascript
var traverse = require('traverse');

var obj = {
    a : [1,2,3],
    b : 4,
    c : [5,6],
    d : { e : [7,8], f : 9 },
};

var leaves = traverse(obj).reduce(function (acc, x) {
    if (this.isLeaf) acc.push(x);
    return acc;
}, []);

console.dir(leaves);
````

Output:

    [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

context
=======

Each method that takes a callback has a context (its `this` object) with these
attributes:

this.node
---------

The present node on the recursive walk

this.path
---------

An array of string keys from the root to the present node

this.parent
-----------

The context of the node's parent.
This is `undefined` for the root node.

this.key
--------

The name of the key of the present node in its parent.
This is `undefined` for the root node.

this.isRoot, this.notRoot
-------------------------

Whether the present node is the root node

this.isLeaf, this.notLeaf
-------------------------

Whether or not the present node is a leaf node (has no children)

this.level
----------

Depth of the node within the traversal

this.circular
-------------

If the node equals one of its parents, the `circular` attribute is set to the
context of that parent and the traversal progresses no deeper.

this.update(value)
------------------

Set a new value for the present node.

this.remove()
-------------

Remove the current element from the output. If the node is in an Array it will
be spliced off. Otherwise it will be deleted from its parent.

this.delete()
-------------

Delete the current element from its parent in the output. Calls `delete` even on
Arrays.

this.before(fn)
---------------

Call this function before any of the children are traversed.

this.after(fn)
--------------

Call this function after any of the children are traversed.

this.pre(fn)
------------

Call this function before each of the children are traversed.

this.post(fn)
-------------

Call this function after each of the children are traversed.

methods
=======

.map(fn)
--------

Execute `fn` for each node in the object and return a new object with the
results of the walk. To update nodes in the result use `this.update(value)`.

.forEach(fn)
------------

Execute `fn` for each node in the object but unlike `.map()`, when
`this.update()` is called it updates the object in-place.

.reduce(fn, acc)
----------------

For each node in the object, perform a
[left-fold](http://en.wikipedia.org/wiki/Fold_(higher-order_function))
with the return value of `fn(acc, node)`.

If `acc` isn't specified, `acc` is set to the root object for the first step
and the root element is skipped.

.deepEqual(obj)
---------------

Returns a boolean, whether the instance value is equal to the supplied object
along a deep traversal using some opinionated choices.

Some notes:

* RegExps are equal if their .toString()s match, but not functions since
functions can close over different variables.

* Date instances are compared using `.getTime()` just like `assert.deepEqual()`.

* Circular references must refer to the same paths within the data structure for
both objects. For instance, in this snippet:

````javascript
var a = [1];
a.push(a); // a = [ 1, *a ]

var b = [1];
b.push(a); // b = [ 1, [ 1, *a ] ]
````

`a` is not the same as `b` since even though the expansion is the same, the
circular references in each refer to different paths into the data structure.

However, in:

````javascript
var c = [1];
c.push(c); // c = [ 1, *c ];
````

`c` is equal to `a` in a `deepEqual()` because they have the same terminal node
structure.

* Arguments objects are not arrays and neither are they the same as regular
objects.

* Instances created with `new` of String, Boolean, and Number types are never
equal to the native versions.

.paths()
--------

Return an `Array` of every possible non-cyclic path in the object.
Paths are `Array`s of string keys.

.nodes()
--------

Return an `Array` of every node in the object.

.clone()
--------

Create a deep clone of the object.

installation
============

Using npm:
    npm install traverse

Or check out the repository and link your development copy:
    git clone http://github.com/substack/js-traverse.git
    cd js-traverse
    npm link .

You can test traverse with "expresso":http://github.com/visionmedia/expresso
(`npm install expresso`):
    js-traverse $ expresso
    
    100% wahoo, your stuff is not broken!

hash transforms
===============

This library formerly had a hash transformation component. It has been
[moved to the hashish package](https://github.com/substack/node-hashish).