-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #14 from blackflux/dev
[Gally]: master <- dev
- Loading branch information
Showing
4 changed files
with
163 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,41 @@ | ||
const contains = (haystack, needle) => { | ||
const needleType = typeof needle; | ||
const haystackType = typeof haystack; | ||
if (needleType !== haystackType) { | ||
return false; | ||
} | ||
if (needleType === 'object') { | ||
const needleIsArray = Array.isArray(needle); | ||
const haystackIsArray = Array.isArray(haystack); | ||
if (needleIsArray !== haystackIsArray) { | ||
return false; | ||
const objectScan = require('object-scan'); | ||
|
||
const scanner = objectScan(['**'], { | ||
rtn: 'context', | ||
abort: true, | ||
breakFn: ({ | ||
isLeaf, isMatch, property, value, context | ||
}) => { | ||
const { stack } = context; | ||
const last = stack[stack.length - 1]; | ||
if (isMatch && !(property in last)) { | ||
context.result = false; | ||
return true; | ||
} | ||
// exact match for arrays | ||
if (needleIsArray) { | ||
if (needle.length !== haystack.length) { | ||
return false; | ||
const current = isMatch ? last[property] : last; | ||
if (isLeaf) { | ||
if (value !== current) { | ||
context.result = false; | ||
return true; | ||
} | ||
return needle.every((e, idx) => contains(haystack[idx], e)); | ||
} else if ( | ||
value instanceof Object !== current instanceof Object | ||
|| Array.isArray(value) !== Array.isArray(current) | ||
|| (Array.isArray(value) && value.length !== current.length) | ||
) { | ||
context.result = false; | ||
return true; | ||
} | ||
// subset match for object | ||
return Object.keys(needle).every((key) => contains(haystack[key], needle[key])); | ||
stack.push(current); | ||
return false; | ||
}, | ||
filterFn: ({ context }) => { | ||
context.stack.pop(); | ||
return context.result !== true; | ||
} | ||
// default comparison | ||
return haystack === needle; | ||
}; | ||
}); | ||
|
||
module.exports = contains; | ||
module.exports = (tree, subtree) => { | ||
const { result } = scanner(subtree, { stack: [tree], result: true }); | ||
return result; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,93 @@ | ||
const expect = require('chai').expect; | ||
const { describe } = require('node-tdd'); | ||
const contains = require('../../src/core/contains'); | ||
|
||
describe('Testing contains', () => { | ||
it('Testing String', () => { | ||
expect(contains('value1', 'value1')).to.equal(true); | ||
expect(contains('value1', 'value2')).to.equal(false); | ||
describe('Testing contains', { timeout: 100000 }, () => { | ||
it('Batch test', ({ fixture }) => { | ||
const gen = fixture('gen'); | ||
const containsRec = fixture('contains-rec'); | ||
for (let x = 0; x < 10000; x += 1) { | ||
const tree = gen(); | ||
const subtree = gen(); | ||
expect(contains(tree, subtree)).to.equal(containsRec(tree, subtree)); | ||
} | ||
}); | ||
|
||
it('Testing List', () => { | ||
expect(contains([1, 2, 3], [1, 2, 3])).to.equal(true); | ||
expect(contains([{ key: 'value1' }], [{ key: 'value1' }])).to.equal(true); | ||
expect(contains([{ key: 'value1' }], [{ key: 'value2' }])).to.equal(false); | ||
expect(contains([1, 2, 3], [3, 2, 1])).to.equal(false); | ||
expect(contains([1, 2, 3], [1, 2])).to.equal(false); | ||
expect(contains([], [])).to.equal(true); | ||
describe('Testing String', () => { | ||
it('Testing equal', () => { | ||
expect(contains('value1', 'value1')).to.equal(true); | ||
}); | ||
it('Testing not equal', () => { | ||
expect(contains('value1', 'value2')).to.equal(false); | ||
}); | ||
}); | ||
|
||
it('Testing Object', () => { | ||
expect(contains({}, {})).to.equal(true); | ||
expect(contains({ key: 'value1' }, { key: 'value1' })).to.equal(true); | ||
expect(contains({ key: 'value1' }, { key: 'value2' })).to.equal(false); | ||
describe('Testing Array', () => { | ||
it('Testing equal arrays (containing three equal numbers)', () => { | ||
expect(contains([1, 2, 3], [1, 2, 3])).to.equal(true); | ||
}); | ||
it('Testing not equal arrays (containing three equal numbers in different order)', () => { | ||
expect(contains([1, 2, 3], [3, 2, 1])).to.equal(false); | ||
}); | ||
it('Testing equal arrays (containing single object)', () => { | ||
expect(contains([{ key: 'value1' }], [{ key: 'value1' }])).to.equal(true); | ||
}); | ||
it('Testing not equal arrays (containing different single object)', () => { | ||
expect(contains([{ key: 'value1' }], [{ key: 'value2' }])).to.equal(false); | ||
}); | ||
it('Testing not equal arrays (array contains number that are a subset)', () => { | ||
expect(contains([1, 2, 3], [1, 2])).to.equal(false); | ||
}); | ||
it('Testing empty arrays equal', () => { | ||
expect(contains([], [])).to.equal(true); | ||
}); | ||
it('Testing nested empty arrays equal', () => { | ||
expect(contains([[], []], [[], []])).to.equal(true); | ||
}); | ||
it('Testing nested not equal (different arrays, removed)', () => { | ||
expect(contains([['x'], []], [[], []])).to.equal(false); | ||
}); | ||
it('Testing nested not equal (different arrays, added)', () => { | ||
expect(contains([[], []], [['x'], []])).to.equal(false); | ||
}); | ||
}); | ||
|
||
it('Testing Type Mismatch', () => { | ||
expect(contains({}, '')).to.equal(false); | ||
expect(contains({}, [])).to.equal(false); | ||
describe('Testing Object', () => { | ||
it('Testing empty objects equal', () => { | ||
expect(contains({}, {})).to.equal(true); | ||
}); | ||
it('Testing objects equal with single key', () => { | ||
expect(contains({ key: 'value1' }, { key: 'value1' })).to.equal(true); | ||
}); | ||
it('Testing objects different with same keys, but different values', () => { | ||
expect(contains({ key: 'value1' }, { key: 'value2' })).to.equal(false); | ||
}); | ||
it('Testing different keys (added)', () => { | ||
expect(contains({ key: 'value1' }, { key: 'value1', foo: 'bar' })).to.equal(false); | ||
}); | ||
it('Testing different keys (removed)', () => { | ||
expect(contains({ key: 'value1', foo: 'bar' }, { key: 'value1' })).to.equal(true); | ||
}); | ||
}); | ||
|
||
describe('Testing Type Mismatch', () => { | ||
it('Testing empty object vs empty string', () => { | ||
expect(contains({}, '')).to.equal(false); | ||
}); | ||
it('Testing empty object vs empty array', () => { | ||
expect(contains({}, [])).to.equal(false); | ||
}); | ||
it('Testing empty array vs empty string', () => { | ||
expect(contains([], '')).to.equal(false); | ||
}); | ||
it('Testing empty array vs empty object', () => { | ||
expect(contains([], {})).to.equal(false); | ||
}); | ||
it('Testing empty string vs empty object', () => { | ||
expect(contains('', {})).to.equal(false); | ||
}); | ||
it('Testing empty string vs empty array', () => { | ||
expect(contains('', [])).to.equal(false); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
const containsRec = (haystack, needle) => { | ||
const needleType = typeof needle; | ||
const haystackType = typeof haystack; | ||
if (needleType !== haystackType) { | ||
return false; | ||
} | ||
if (needleType === 'object') { | ||
const needleIsArray = Array.isArray(needle); | ||
const haystackIsArray = Array.isArray(haystack); | ||
if (needleIsArray !== haystackIsArray) { | ||
return false; | ||
} | ||
// exact match for arrays | ||
if (needleIsArray) { | ||
if (needle.length !== haystack.length) { | ||
return false; | ||
} | ||
return needle.every((e, idx) => containsRec(haystack[idx], e)); | ||
} | ||
// subset match for object | ||
return Object.keys(needle).every((key) => containsRec(haystack[key], needle[key])); | ||
} | ||
// default comparison | ||
return haystack === needle; | ||
}; | ||
|
||
module.exports = containsRec; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
const rand = (values) => Math.floor(Math.random() * values); | ||
|
||
const gen = (depth) => { | ||
const v = rand(3); | ||
if (v === 0 || depth >= 3) { | ||
return rand(3); | ||
} | ||
if (v === 1) { | ||
return Array.from({ length: rand(3) }, () => gen(depth + 1)); | ||
} | ||
const r = {}; | ||
for (let idx = 0, len = gen(3) + 1; idx < len; idx += 1) { | ||
r[['A', 'B', 'C'][idx]] = gen(depth + 1); | ||
} | ||
return r; | ||
}; | ||
|
||
module.exports = () => gen(0); |