mirror of
				https://github.com/flynx/walk.js.git
				synced 2025-10-30 19:40:11 +00:00 
			
		
		
		
	moves walk from object-diff.js...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
		
							parent
							
								
									53cc93831f
								
							
						
					
					
						commit
						d610447daa
					
				
							
								
								
									
										185
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										185
									
								
								README.md
									
									
									
									
									
								
							| @ -1,2 +1,187 @@ | |||||||
| # walk.js | # walk.js | ||||||
|  | 
 | ||||||
| An extensible tree walk(..) framework... | An extensible tree walk(..) framework... | ||||||
|  | 
 | ||||||
|  | ## Theory and operation | ||||||
|  | 
 | ||||||
|  | This module generalizes structure traverse (*walking*). This is done via a `walk(..)` function that recieves a user-defined `getter(..)` and returns a *walker*. | ||||||
|  | 
 | ||||||
|  | `walk(getter)(state, ...nodes) -> state`   | ||||||
|  | `walk(getter, state)(...nodes) -> state`   | ||||||
|  | `walk(getter, state, ...nodes) -> state`   | ||||||
|  | - Recieves a `getter` function a `state` and a list of `nodes`, | ||||||
|  | - Iterates through `nodes` calling the `getter(..)` per node, threading the `state` through each call, | ||||||
|  | - Returns the `state` when there are no more `nodes`. | ||||||
|  | 
 | ||||||
|  | `getter(state, node, next, down, stop) -> state`   | ||||||
|  | - Recieves `state`, `node` and three control functions: `next`, `down` and `stop`, | ||||||
|  | - Can process `node` and `state`, | ||||||
|  | - Can queue nodes for walking via `next(...nodes)` | ||||||
|  | - Can walk nodes directly via `down(state, ...nodes) -> state` | ||||||
|  | - Can abbort *walking* and return a state via `stop()` or `stop(state)` | ||||||
|  | - Returns `state`, | ||||||
|  | 
 | ||||||
|  | A trivial *flat* example... | ||||||
|  | ```javascript | ||||||
|  | walk(function(r, n){ return r+n }, 0, ...[1,2,3]) // -> 6 | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | The above is essentially equivalent to... | ||||||
|  | ```javascript | ||||||
|  | [1,2,3].reduce(function(r, n){ return r+n }, 0) // -> 6 | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | And for trivial or *flat* lists `.reduce(..)` and friends are simpler and more logical. | ||||||
|  | 
 | ||||||
|  | Target use-cases: | ||||||
|  | - The input is not *flat*: | ||||||
|  | 	```javascript | ||||||
|  | 	var sum = walk(function(r, n){ | ||||||
|  | 		return n instanceof Array ? | ||||||
|  | 			down(r, ...n) | ||||||
|  | 			: r + n }, 0)  | ||||||
|  | 			 | ||||||
|  | 	sum( [1, [2, 3], 4, [[5], 6]] ) // -> 21 | ||||||
|  | 	``` | ||||||
|  | - Need to abort the recursion prematurelly: | ||||||
|  | 	```javascript | ||||||
|  | 	var containsZero = walk(function(r, e, next, down, stop){ | ||||||
|  | 		return e === 0 ?  | ||||||
|  | 				// target found, abort the search... | ||||||
|  | 				stop(true) | ||||||
|  | 			: e instanceof Array ? | ||||||
|  | 				!!next(...e) | ||||||
|  | 			: r }, false) | ||||||
|  | 
 | ||||||
|  | 	containsZero( [1, [2, 0], 4, [[5], 6]] ) // -> true | ||||||
|  | 	containsZero( [1, [2, 5], 4, [[5], 6]] ) // -> false | ||||||
|  | 	``` | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Installation and loading | ||||||
|  | 
 | ||||||
|  | ```shell | ||||||
|  | $ npm install --save generic-walk | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ```javascript | ||||||
|  | var walk = require('generic-walk').walk | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | *Note: This module supports both AMD and node's `require(..)`** | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## API | ||||||
|  | 
 | ||||||
|  | `walk(getter(..)) -> walker(state, ...nodes)`   | ||||||
|  | Construct a reusable walker. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | `walk(getter(..), state) -> walker(...nodes)`   | ||||||
|  | Construct a reusable walker with fixed initial state. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | `walk(getter(..), state, ...nodes) -> result`   | ||||||
|  | Walk the nodes. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ### The getter | ||||||
|  | 
 | ||||||
|  | `getter(state, node, next(..), down(..), stop(..)) -> state`   | ||||||
|  | User provided function, called to process a node. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | `next(...nodes)`   | ||||||
|  | Queue `nodes` for walking. The queued nodes will get *walked* after this level of nodes is done (i.e. the `getter(..)` is called for each node on level). | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | `down(state, ...nodes) -> state`   | ||||||
|  | Walk `nodes` and return `state`. The nodes will get *walked* immidiately. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | `stop()`   | ||||||
|  | `stop(state)`   | ||||||
|  | Stop walking and return `state`. The passed `state` is directly returned from the *walker*. | ||||||
|  | 
 | ||||||
|  | *Note that `stop(..)` behaves in a similar manner to `return`, i.e. execution is aborted immidiately.* | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Examples | ||||||
|  | 
 | ||||||
|  | Sum all the values of a nested array (breadth-first)... | ||||||
|  | ```javascript | ||||||
|  | var sum = walk(function(res, node, next){ | ||||||
|  | 	return node instanceof Array ? | ||||||
|  | 		// compensate for that next(..) returns undefined... | ||||||
|  | 		next(...node)  | ||||||
|  | 			|| res | ||||||
|  | 		: res + node }, 0) | ||||||
|  | 
 | ||||||
|  | sum([1, [2], 3, [[4, 5]]]) // -> 15 ...walks the nodes: 1, 3, 2, 4, 5 | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Sum all the values of a nested array (depth-first)... | ||||||
|  | ```javascript | ||||||
|  | var sumd = walk(function(res, node, next, down, stop){ | ||||||
|  | 	return node instanceof Array ? | ||||||
|  | 		down(res, ...node) | ||||||
|  | 		: res + node }, 0) | ||||||
|  | 
 | ||||||
|  | sumd([1, [2], 3, [[4, 5]]]) // -> 15 ...walks the nodes: 1, 2, 3, 4, 5 | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | FInd first zero in tree and return it's path... | ||||||
|  | ```javascript | ||||||
|  | // XXX res/path threading seem unnatural here... | ||||||
|  | var __firstZero = walk(function(res, node, next, down, stop){ | ||||||
|  | 	var k = node[0] | ||||||
|  | 	var v = node[1] | ||||||
|  | 	var path = res[0] | ||||||
|  | 	return v === 0 ? | ||||||
|  | 			// NOTE: we .slice(1) here to remove the initial null | ||||||
|  | 			//		we added in firstZero(..)... | ||||||
|  | 			stop([ path.slice(1).concat([k]) ]) | ||||||
|  | 		: v instanceof Object? | ||||||
|  | 			down( | ||||||
|  | 				[path.concat([k]), null],  | ||||||
|  | 				...Object.entries(v)) | ||||||
|  | 		: res }, [[], null]) | ||||||
|  | var firstZero = function(value){  | ||||||
|  | 	// NOTE: we are wrapping the input here to make it the same  | ||||||
|  | 	//		format as that of Object.entries(..) items... | ||||||
|  | 	return __firstZero([null, value]).pop() } | ||||||
|  | 
 | ||||||
|  | firstZero([10, 5, [{x: 1, y: 0}, 4]]) // -> ['2', '0', 'y'] | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | FInd first zero in tree and return it's path... | ||||||
|  | ```javascript | ||||||
|  | // same as the above but illustrates a different strategy, a bit | ||||||
|  | // cleaner but creates a walker every time it's called... | ||||||
|  | var firstZero = function(value){ | ||||||
|  | 	return walk( | ||||||
|  | 		function(res, node, next, down, stop){ | ||||||
|  | 			var k = node[0] | ||||||
|  | 			var v = node[1] | ||||||
|  | 			var path = res[0] | ||||||
|  | 			return v === 0 ? | ||||||
|  | 					// NOTE: we .slice(1) here to remove the initial null | ||||||
|  | 					//		we added in firstZero(..)... | ||||||
|  | 					stop([ path.slice(1).concat([k]) ]) | ||||||
|  | 				: v instanceof Object? | ||||||
|  | 					down( | ||||||
|  | 						[path.concat([k]), null],  | ||||||
|  | 						...Object.entries(v)) | ||||||
|  | 				: res },  | ||||||
|  | 		[[], null],  | ||||||
|  | 		// wrap the input to make it compatible with Object.entries(..)  | ||||||
|  | 		// items... | ||||||
|  | 		[null, value]) | ||||||
|  | 			// separate the result from path... | ||||||
|  | 			.pop() } | ||||||
|  | 
 | ||||||
|  | firstZero([10, 5, [{x: 1, y: 0}, 4]]) // -> ['2', '0', 'y'] | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user