Skip to content

Commit

Permalink
updated readme and examples for the new interface changes
Browse files Browse the repository at this point in the history
  • Loading branch information
James Halliday committed Feb 18, 2011
1 parent c89ae4b commit aa2d4f3
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 178 deletions.
271 changes: 153 additions & 118 deletions README.markdown
Original file line number Diff line number Diff line change
@@ -1,147 +1,182 @@
Description
===========
Traverse and transform objects by visiting every node on a recursive walk.
traverse
========

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

Using npm:
npm install traverse
examples
========

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

You can test traverse with "expresso":http://github.com/visionmedia/expresso
(`npm install expresso`):
js-traverse $ expresso
negative.js
var Traverse = require('traverse');
var obj = [ 5, 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];

100% wahoo, your stuff is not broken!
Traverse(obj).forEach(function (x) {
if (x < 0) this.update(x + 128);
});

console.dir(obj);

Examples
========

These examples use node.js, but the module should work without it.
output
[ 5, 6, 125, [ 7, 8, 126, 1 ], { f: 10, g: 115 } ]

Collect Leaf Nodes
collect leaf nodes
------------------
var sys = require('sys');

leaves.js
var Traverse = require('traverse');

var acc = [];
Traverse({

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

var leaves = Traverse(obj).reduce(function (acc, x) {
if (this.isLeaf) acc.push(x);
});
sys.puts(acc.join(' '));

/* Output:
1 2 3 4 5 6 7 8 9
*/
return acc;
}, []);

Replace Negative Numbers
------------------------
var sys = require('sys');
var Traverse = require('traverse');

var numbers = [ 5, 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];
var fixed = Traverse.map(numbers, function (x) {
if (typeof x == 'number' && x < 0) this.update(x + 128);
});
sys.puts(sys.inspect(fixed));

/* Output:
[ 5, 6, 124, [ 7, 8, 125, 1 ], { f: 10, g: 114 } ]
*/

Scrub and Collect Functions
---------------------------

Suppose you have a complex data structure that you want to send to another
process with JSON over a network socket. If the data structure has references to
functions in it, JSON will convert functions inside Arrays to null and ignore
keys that map to functions inside Objects.

> JSON.stringify([ 7, 8, function () {}, 9, { b : 4, c : function () {} } ])
'[7,8,null,9,{"b":4}]'

If these nested functions are important, it'd be nice if they could be collected
and replaced with some placeholder value that JSON can encapsulate. This sort of
transform and collection might be useful for
[an asynchronous remote method invocation library
](http://github.com/substack/dnode), for instance.

This example scrubs functions out of an arbitrary data structure so that the
structure may be JSON-ified. The functions are also collected for some other
use.

var sys = require('sys');
var Traverse = require('traverse');

var id = 54;
var callbacks = {};
var obj = { moo : function () {}, foo : [2,3,4, function () {}] };

var scrubbed = Traverse.map(obj, function (x) {
if (typeof x == 'function') {
callbacks[id] = { id : id, f : x, path : this.path };
this.update('[Function]');
id++;
}
});

sys.puts(JSON.stringify(scrubbed));
sys.puts(sys.inspect(callbacks));

/* Output:
{"moo":"[Function]","foo":[2,3,4,"[Function]"]}
{ '54': { id: 54, f: [Function], path: [ 'moo' ] }
, '55': { id: 55, f: [Function], path: [ 'foo', '3' ] }
}
*/

Hash Transforms
===============
output
[ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

There's also a hash lib in this distribution with tons of useful functions to
operate on hashes:
context
=======

map, forEach, filter, reduce, some, update, merge, tap, valuesAt, extract,
items, keys, values, clone, copy
Each method that takes a callback has a context (its `this` object) with these
attributes:

These work mostly like their array counterparts where available except they get
an extra second argument, key.
node
----

Other functions like tap, valuesAt, merge, and update work like their ruby
counterparts.
The present node on the recursive walk

The extract function creates a hash with only the supplied keys in it.
path
----

The clone property makes a deep copy with Traversable.clone() and the copy
property makes a shallow copy.
An array of string keys from the root to the present node

The items property is the hash with the Hash() wrapper peeled away.
parent
------

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

key
---

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

> var Hash = require('traverse/hash')
> Hash({ a : 1, b : 2 }).map(function (v) { return v + 1 }).items
{ a: 2, b: 3 }

To do the same thing without chaining:
isRoot, notRoot
---------------

Whether the present node is the root node

isLeaf, notLeaf
---------------

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

level
-----

Depth of the node within the traversal

> Hash.map({ a : 1, b : 2 }, function (v) { return v + 1 })
{ a: 2, b: 3 }
circular
--------

The 'this' context of the function calls is the same object that the chained
functions return, so you can make nested chains.
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.

update(value)
-------------

Set a new value for the present node.

before(fn)
----------

Call this function before any of the children are traversed.

after(fn)
---------

Call this function after any of the children are traversed.

pre(fn)
-------

Call this function before each of the children are traversed.

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.

.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
===============

See also [creationix's pattern/hash](http://github.com/creationix/pattern),
which does a similar thing except with hash inputs and array outputs.
This library formerly had a hash transformation component. It has been
[moved to the hashish package](https://github.com/substack/node-hashish).
25 changes: 0 additions & 25 deletions examples/hash.js

This file was deleted.

19 changes: 5 additions & 14 deletions examples/json.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
#!/usr/bin/env node
var sys = require('sys');
var Traverse = require('traverse');

var id = 54;
var callbacks = {};
var obj = { moo : function () {}, foo : [2,3,4, function () {}] };

var scrubbed = Traverse(obj).modify(function (x) {
if (x instanceof Function) {
var scrubbed = Traverse(obj).map(function (x) {
if (typeof x === 'function') {
callbacks[id] = { id : id, f : x, path : this.path };
this.update('[Function]');
id++;
}
}).get();
});

sys.puts(JSON.stringify(scrubbed));
sys.puts(sys.inspect(callbacks));

/* Output:
{"moo":"[Function]","foo":[2,3,4,"[Function]"]}
{ '54': { id: 54, f: [Function], path: [ 'moo' ] }
, '55': { id: 55, f: [Function], path: [ 'foo', '3' ] }
}
*/
console.dir(scrubbed);
console.dir(callbacks);
19 changes: 8 additions & 11 deletions examples/leaves.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
#!/usr/bin/env node
var sys = require('sys');
var Traverse = require('traverse');

var acc = [];
Traverse({
var obj = {
a : [1,2,3],
b : 4,
c : [5,6],
d : { e : [7,8], f : 9 }
}).forEach(function (x) {
d : { e : [7,8], f : 9 },
};

var leaves = Traverse(obj).reduce(function (acc, x) {
if (this.isLeaf) acc.push(x);
});
sys.puts(acc.join(' '));
return acc;
}, []);

/* Output:
1 2 3 4 5 6 7 8 9
*/
console.dir(leaves);
Loading

0 comments on commit aa2d4f3

Please sign in to comment.