Skip to content

Commit

Permalink
Adding isOptimistic option to traverse.
Browse files Browse the repository at this point in the history
  • Loading branch information
DarrenPaulWright committed Feb 17, 2019
1 parent ad332fe commit 77c7f23
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 11 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)

## [0.2.5] - 2019-2-17
### Changed
- Added isOptimistic option to [traverse](docs/traverse.md)

## [0.2.4] - 2019-2-15
### Changed
- Fixed some dependency issues
Expand Down Expand Up @@ -39,6 +43,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [diffUpdate](docs/diffUpdate.md)
- [intersection](docs/intersection.md)

[0.2.5]: https://github.com/DarrenPaulWright/object-agent/compare/v0.2.4...0.2.5
[0.2.4]: https://github.com/DarrenPaulWright/object-agent/compare/v0.2.3...0.2.4
[0.2.3]: https://github.com/DarrenPaulWright/object-agent/compare/v0.2.2...0.2.3
[0.2.2]: https://github.com/DarrenPaulWright/object-agent/compare/v0.2.1...0.2.2
Expand Down
13 changes: 7 additions & 6 deletions docs/traverse.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@ A javascript library for working with objects

<a name="traverse"></a>

## traverse(object, callback) ⇒ <code>Boolean</code>
Traverses a nested object. The traversal stops as soon as the callback returns a truthy value.
## traverse(object, callback, [isOptimistic]) ⇒ <code>Boolean</code>
Traverses a nested object.

**Kind**: global function
**Returns**: <code>Boolean</code> - true if the callback function returns a truthy value for any path; otherwise, false.

| Param | Type |
| --- | --- |
| object | <code>Object</code> |
| callback | <code>function</code> |
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| object | <code>Object</code> | | |
| callback | <code>function</code> | | Provides two args, path and value. If true is returned then stop traversing and return true. |
| [isOptimistic] | <code>Boolean</code> | <code>false</code> | If true then returning true in the callback will prevent going deeper down that branch, but will otherwise continue traversing. |

**Example**
``` javascriptimport { traverse } from 'object-agent';const thing = { a: [{ b: 'c' }, { b: 'd' }], e: 'f};traverse(thing, (path, value) => { console.log(path, value);});// => [], { a: [{ b: 'c' }, { b: 'd' }] }// => ['a'], [{ b: 'c' }, { b: 'd' }]// => ['a', 0], { b: 'c' }// => ['a', 0, 'b'], 'c'// => ['a', 1], { b: 'd' }// => ['a', 1, 'b'], 'd'// => ['e'], 'f'```
Expand Down
31 changes: 27 additions & 4 deletions src/traverse.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { isArray, isObject } from 'type-enforcer';
import forOwn from './forOwn';

/**
* Traverses a nested object. The traversal stops as soon as the callback returns a truthy value.
* Traverses a nested object.
*
* @example
* ``` javascript
Expand Down Expand Up @@ -32,11 +32,12 @@ import forOwn from './forOwn';
* @function traverse
*
* @arg {Object} object
* @arg {Function} callback
* @arg {Function} callback - Provides two args, path and value. If true is returned then stop traversing and return true.
* @arg {Boolean} [isOptimistic=false] - If true then returning true in the callback will prevent going deeper down that branch, but will otherwise continue traversing.
*
* @returns {Boolean} true if the callback function returns a truthy value for any path; otherwise, false.
*/
export default (object, callback) => {
export default (object, callback, isOptimistic) => {
const processValue = (path, value) => {
if (callback(path, value)) {
return true;
Expand All @@ -49,5 +50,27 @@ export default (object, callback) => {
}
};

return processValue([], object);
const processValueOptimistic = (path, value) => {
let isCanceled = false;

const loopCallback = (value, key) => {
if (processValueOptimistic(path.concat(key), value)) {
isCanceled = true;
}
};

if (callback(path, value)) {
isCanceled = true;
}
else if (isArray(value)) {
value.some(loopCallback);
}
else if (isObject(value)) {
forOwn(value, loopCallback);
}

return isCanceled;
};

return isOptimistic ? processValueOptimistic([], object) : processValue([], object);
};
107 changes: 106 additions & 1 deletion tests/traverse.Test.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ describe('traverse', () => {
}
if (deepEqual(path, ['test', 0, 'test2', 'test3', 0]) && value === 'string1') {
testVar++;
return true
return true;
}
if (deepEqual(path, ['test', 0, 'test2', 'test3', 1]) && value === 'string2') {
testVar++;
Expand All @@ -141,4 +141,109 @@ describe('traverse', () => {
assert.equal(testVar, 6);
assert.isTrue(isCanceled);
});

describe('isOptimistic', () => {
it('should NOT traverse deeper when true is returned', () => {
let total = 0;
let testVar = 0;
const testObject = {
test: [{
test2: {
test3: ['string1', 'string2']
},
test4: null
}]
};

const isCanceled = traverse(testObject, (path, value) => {
total++;

if (deepEqual(path, [])) {
testVar++;
}
if (deepEqual(path, ['test'])) {
testVar++;
}
if (deepEqual(path, ['test', 0])) {
testVar++;
}
if (deepEqual(path, ['test', 0, 'test2'])) {
testVar++;
return true;
}
if (deepEqual(path, ['test', 0, 'test2', 'test3'])) {
testVar++;
}
if (deepEqual(path, ['test', 0, 'test2', 'test3', 0]) && value === 'string1') {
testVar++;
}
if (deepEqual(path, ['test', 0, 'test2', 'test3', 1]) && value === 'string2') {
testVar++;
}
if (deepEqual(path, ['test', 0, 'test4']) && value === null) {
testVar++;
}
}, true);

assert.equal(total, 5);
assert.equal(testVar, 5);
assert.isTrue(isCanceled);
});

it('should NOT traverse further array items when true is returned', () => {
let total = 0;
let testVar = 0;
const testObject = {
test: [{
test2: {
test3: ['string1', 'string2']
},
test4: null
}, {
test4: null
}]
};

const isCanceled = traverse(testObject, (path, value) => {
total++;

console.log('path: ', path);
if (deepEqual(path, [])) {
testVar++;
}
if (deepEqual(path, ['test'])) {
testVar++;
}
if (deepEqual(path, ['test', 0])) {
testVar++;
return true;
}
if (deepEqual(path, ['test', 0, 'test2'])) {
testVar++;
}
if (deepEqual(path, ['test', 0, 'test2', 'test3'])) {
testVar++;
}
if (deepEqual(path, ['test', 0, 'test2', 'test3', 0]) && value === 'string1') {
testVar++;
}
if (deepEqual(path, ['test', 0, 'test2', 'test3', 1]) && value === 'string2') {
testVar++;
}
if (deepEqual(path, ['test', 0, 'test4']) && value === null) {
testVar++;
}
if (deepEqual(path, ['test', 1])) {
testVar++;
}
if (deepEqual(path, ['test', 1, 'test4'])) {
testVar++;
}
}, true);

assert.equal(total, 5);
assert.equal(testVar, 5);
assert.isTrue(isCanceled);
});
});
});

0 comments on commit 77c7f23

Please sign in to comment.