Created
June 26, 2017 23:04
-
-
Save JoelCodes/e5ec1bee606b27e90b73b02be4ea98d4 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
Show hidden characters
| { | |
| "extends": "airbnb-base", | |
| "plugins": [ | |
| "import" | |
| ] | |
| } |
This file contains hidden or 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
| const { fromArray, stream } = require('./lazy'); | |
| const lFA = fromArray(['Joel', 'Sam', 'Don', 'David']); | |
| const changed = lFA | |
| .filter(name => name[0] !== 'D') | |
| .map(name => name.toUpperCase()); | |
| console.log('Running For Each'); | |
| changed.forEach((val) => { | |
| console.log('Hello,', val); | |
| }); | |
| const streamFrom0 = stream(); | |
| streamFrom0 | |
| .take(50) | |
| .skip(5) | |
| .forEach((val, index) => { | |
| console.log(val, index); | |
| }); | |
| const tenDown = stream(10, val => val - 1).take(10); | |
| const fiveUp = stream().take(5); | |
| tenDown.zip(fiveUp).forEach((vals) => { | |
| console.log(vals); | |
| }); | |
| const nums = fromArray([4, 6, 2, 1, 5]); | |
| const numsOver2 = nums | |
| .filter(val => val > 2); | |
| console.log('Now To Do Something...'); | |
| console.log(numsOver2.reduce((sum, num) => sum + num, 0)); |
This file contains hidden or 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
| const Nil = { | |
| head: undefined, | |
| tail: undefined, | |
| count: 0, | |
| reduce: (reducer, seed) => seed, | |
| any: () => false, | |
| all: () => true, | |
| first: () => undefined, | |
| concat: listFn => listFn(), | |
| toArray: () => [], | |
| at: () => undefined, | |
| forEach: () => {}, | |
| }; | |
| [ | |
| 'filter', 'map', | |
| 'take', 'takeUntil', 'takeWhile', | |
| 'skip', 'skipUntil', 'skipWhile', | |
| 'flatMap', 'zip'] | |
| .forEach((methodName) => { | |
| Nil[methodName] = () => Nil; | |
| }); | |
| Object.freeze(Nil); | |
| const Cons = (head, tail = () => Nil) => { | |
| const list = { | |
| head, | |
| reduce: (reducer, seed) => tail().reduce(reducer, reducer(seed, head)), | |
| filter: predicate => (predicate(head) | |
| ? Cons(head, () => tail().filter(predicate)) | |
| : tail().filter(predicate)), | |
| toArray: () => [head].concat(tail().toArray()), | |
| map: transformer => Cons(transformer(head), () => tail().map(transformer)), | |
| all: predicate => predicate(head) && tail().all(predicate), | |
| any: predicate => predicate(head) || tail().any(predicate), | |
| take: amt => (isNaN(amt) || amt <= 0 | |
| ? Nil | |
| : Cons(head, () => tail().take(amt - 1))), | |
| takeUntil: predicate => (predicate(head) | |
| ? Nil | |
| : Cons(head, () => tail().takeUntil(predicate))), | |
| takeWhile: predicate => (predicate(head) | |
| ? Cons(head, () => tail().takeWhile(predicate)) | |
| : Nil), | |
| skip: amt => (isNaN(amt) || amt <= 0 | |
| ? list | |
| : tail().skip(amt - 1)), | |
| skipUntil: predicate => (predicate(head) | |
| ? list | |
| : tail().skipUntil(predicate)), | |
| skipWhile: predicate => (predicate(head) | |
| ? tail().skipWhile(predicate) | |
| : list), | |
| first: predicate => (predicate(head) | |
| ? head | |
| : tail().first(predicate)), | |
| concat: tailFn => Cons(head, () => tail().concat(tailFn)), | |
| zip: otherList => (otherList === Nil | |
| ? Nil | |
| : Cons([head, otherList.head], () => tail().zip(otherList.tail))), | |
| flatMap: transformer => transformer(head) | |
| .concat(() => tail().flatMap(transformer)), | |
| at: index => (isNaN(index) || index <= 0 | |
| ? head | |
| : tail().at(index - 1)), | |
| forEach: (action, index = 0) => { | |
| action(head, index); | |
| tail().forEach(action, index + 1); | |
| }, | |
| }; | |
| Object.defineProperties(list, { | |
| tail: { get: tail }, | |
| count: { | |
| get: () => 1 + tail().count, | |
| }, | |
| }); | |
| return Object.freeze(list); | |
| }; | |
| const fromArray = (array) => { | |
| if (!array || !array.length) { | |
| return Nil; | |
| } | |
| return Cons(array[0], () => fromArray(array.slice(1))); | |
| }; | |
| const stream = (start = 0, incrementer = val => val + 1) => | |
| Cons(start, () => stream(incrementer(start), incrementer)); | |
| module.exports = { | |
| Nil, | |
| Cons, | |
| fromArray, | |
| stream, | |
| }; |
This file contains hidden or 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
| { | |
| "name": "lazy", | |
| "version": "1.0.0", | |
| "description": "", | |
| "main": "index.js", | |
| "scripts": { | |
| "test": "mocha --reporter nyan", | |
| "docs": "mocha --reporter markdown > doc.md", | |
| "lint": "node_modules/.bin/eslint *.js" | |
| }, | |
| "keywords": [], | |
| "author": "", | |
| "license": "ISC", | |
| "devDependencies": { | |
| "eslint": "^3.19.0", | |
| "eslint-config-airbnb": "^15.0.1", | |
| "eslint-config-airbnb-base": "^11.2.0", | |
| "eslint-config-google": "^0.8.0", | |
| "eslint-plugin-import": "^2.6.0", | |
| "eslint-plugin-jsx-a11y": "^5.0.3", | |
| "eslint-plugin-react": "^7.1.0" | |
| }, | |
| "dependencies": { | |
| "chai": "^4.0.2", | |
| "mocha": "^3.4.2" | |
| } | |
| } |
This file contains hidden or 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
| /* eslint-env mocha */ | |
| /* eslint no-unused-expressions:0 */ | |
| const { expect } = require('chai'); | |
| const { Nil, Cons, fromArray, stream } = require('./lazy'); | |
| describe('Nil', () => { | |
| it('has undefined, immutable head and tail properties and a count of 0', () => { | |
| expect(Nil).to.haveOwnProperty('head'); | |
| Nil.head = 3; | |
| expect(Nil.head).to.be.undefined; | |
| expect(Nil).to.haveOwnProperty('tail'); | |
| Nil.tail = 3; | |
| expect(Nil.tail).to.be.undefined; | |
| Nil.count = 3; | |
| expect(Nil.count).to.eq(0); | |
| }); | |
| describe('#toArray():ListType[]', () => { | |
| expect(Nil.toArray()).to.deep.eq([]); | |
| }); | |
| describe('#reduce(reducer:(AggType, ListType) => AggType, seed:AggType):AggType', () => { | |
| it('returns the seed', () => { | |
| expect(Nil.reduce((val, item) => val + item, 8)).to.eq(8); | |
| }); | |
| }); | |
| describe('#filter(predicate: (any) => bool):List', () => { | |
| it('returns Nil', () => { | |
| expect(Nil.filter(() => true)).to.eq(Nil); | |
| }); | |
| }); | |
| describe('#map(transformer: (any) => any):List', () => { | |
| it('returns Nil', () => { | |
| expect(Nil.map(a => a + 1)).to.eq(Nil); | |
| }); | |
| }); | |
| describe('#any(predicate:(ListType) => bool):bool', () => { | |
| it('returns false', () => { | |
| expect(Nil.any(() => true)).to.be.false; | |
| }); | |
| }); | |
| describe('#all(predicate:(ListType) => bool):bool', () => { | |
| it('returns true', () => { | |
| expect(Nil.all(() => true)).to.be.true; | |
| }); | |
| }); | |
| describe('#take(amount:Int):List', () => { | |
| it('returns Nil', () => { | |
| expect(Nil.take(4)).to.eq(Nil); | |
| }); | |
| }); | |
| describe('#takeWhile(predicate:(ListType) => bool):List', () => { | |
| it('returns Nil', () => { | |
| expect(Nil.takeWhile(() => true)).to.eq(Nil); | |
| }); | |
| }); | |
| describe('#takeUntil(predicate:(ListType) => bool):List', () => { | |
| it('returns Nil', () => { | |
| expect(Nil.takeUntil(() => false)).to.eq(Nil); | |
| }); | |
| }); | |
| describe('#skip(amount:int):List', () => { | |
| it('returns Nil', () => { | |
| expect(Nil.skip(4)).to.eq(Nil); | |
| }); | |
| }); | |
| describe('#skipWhile(predicate:(any) => bool):List', () => { | |
| it('returns Nil', () => { | |
| expect(Nil.skipWhile(() => false)).to.eq(Nil); | |
| }); | |
| }); | |
| describe('#skipUntil(predicate:(any) => bool):List', () => { | |
| it('returns Nil', () => { | |
| expect(Nil.skipUntil(() => true)).to.eq(Nil); | |
| }); | |
| }); | |
| describe('#first(predicate:(any) => bool):any', () => { | |
| it('returns undefined', () => { | |
| expect(Nil.first(() => true)).to.be.undefined; | |
| }); | |
| }); | |
| describe('#concat(() => List):', () => { | |
| it('returns the new list', () => { | |
| const newList = Cons(3); | |
| expect(Nil.concat(() => newList)).to.eq(newList); | |
| }); | |
| }); | |
| describe('#zip(List)', () => { | |
| it('returns Nil', () => { | |
| expect(Nil.zip(Cons(3))).to.eq(Nil); | |
| }); | |
| }); | |
| describe('#flatMap((item) => List): List', () => { | |
| it('returns Nil', () => { | |
| expect(Nil.flatMap(() => Cons)).to.eq(Nil); | |
| }); | |
| }); | |
| describe('#at(index):any', () => { | |
| it('returns undefined', () => { | |
| expect(Nil.at(0)).to.be.undefined; | |
| }); | |
| }); | |
| describe('#forEach((val) => void):void', () => { | |
| const action = () => { throw new Error('This Should Not Have Run'); }; | |
| Nil.forEach(action); | |
| }); | |
| }); | |
| describe('Cons(head: any, tail: () => List)', () => { | |
| it('has set, immutable head and tail properties and an appropriate count', () => { | |
| const list = Cons(3, () => Nil); | |
| expect(list).to.haveOwnProperty('head'); | |
| list.head = 4; | |
| expect(list.head).to.eq(3); | |
| expect(list).to.haveOwnProperty('tail'); | |
| list.tail = 3; | |
| expect(list.tail).to.eq(Nil); | |
| expect(list).to.haveOwnProperty('count'); | |
| list.count = 2; | |
| expect(list.count).to.eq(1); | |
| expect(Cons(3, () => Cons(4)).count).to.eq(2); | |
| }); | |
| describe('#toArray()', () => { | |
| expect(Cons(3, () => Cons(4)).toArray()).to.deep.eq([3, 4]); | |
| }); | |
| describe('#reduce(reducer, seed)', () => { | |
| it('returns the reduced value', () => { | |
| const list = Cons(3, () => Cons(4)); | |
| expect(list.reduce((val, item) => val * item, 2)).to.eq(24); | |
| }); | |
| }); | |
| describe('#filter(predicate)', () => { | |
| it('returns an empty list if none match', () => { | |
| expect(Cons(3, () => Cons(4)).filter(() => false)).to.eq(Nil); | |
| }); | |
| it('returns a shortened list', () => { | |
| const list = Cons(3, () => Cons(4, () => Cons(5))) | |
| .filter(val => val !== 4); | |
| expect(list.toArray()).to.deep.eq([3, 5]); | |
| }); | |
| }); | |
| describe('#map(transformer)', () => { | |
| it('returns a transformed list', () => { | |
| expect(Cons(3, () => Cons(4)).map(val => -val).toArray()).to.deep.eq([-3, -4]); | |
| }); | |
| }); | |
| describe('#all(predicate)', () => { | |
| it('returns true if all are true', () => { | |
| expect(Cons(4, () => Cons(3)).all(() => true)).to.be.true; | |
| }); | |
| it('returns false if any fail the predicate', () => { | |
| expect(Cons(4, () => Cons(3)).all(val => val !== 3)).to.be.false; | |
| }); | |
| }); | |
| describe('#any(predicate', () => { | |
| it('returns false if all are false', () => { | |
| expect(Cons(4, () => Cons(3)).any(() => false)).to.be.false; | |
| }); | |
| it('returns true if any pass', () => { | |
| expect(Cons(4, () => Cons(3)).any(val => val === 3)).to.be.true; | |
| }); | |
| }); | |
| describe('#take(amount)', () => { | |
| it('returns Nil with #take(0)', () => { | |
| const list = Cons(4, () => Cons(3)); | |
| expect(list.take(0)).to.eq(Nil); | |
| }); | |
| it('returns a list with the appropriate items', () => { | |
| const list = Cons(4, () => Cons(3)); | |
| expect(list.take(3).toArray()).to.deep.eq([4, 3]); | |
| expect(list.take(1).toArray()).to.deep.eq([4]); | |
| }); | |
| }); | |
| describe('#takeUntil(predicate)', () => { | |
| it('returns Nil with true predicate', () => { | |
| expect(Cons(4).takeUntil(() => true)).to.eq(Nil); | |
| }); | |
| it('returns a list with the appropriate items', () => { | |
| const list = Cons(4, () => Cons(3)); | |
| expect(list.takeUntil(() => false).toArray()).to.deep.eq([4, 3]); | |
| expect(list.takeUntil(val => val === 3).toArray()).to.deep.eq([4]); | |
| }); | |
| }); | |
| describe('#takeWhile', () => { | |
| it('returns Nil with false', () => { | |
| expect(Cons(4).takeWhile(() => false)).to.eq(Nil); | |
| }); | |
| it('returns a list with the appropriate items', () => { | |
| const list = Cons(4, () => Cons(3)); | |
| expect(list.takeWhile(() => true).toArray()).to.deep.eq([4, 3]); | |
| expect(list.takeWhile(val => val !== 3).toArray()).to.deep.eq([4]); | |
| }); | |
| }); | |
| describe('#skip(amt)', () => { | |
| it('returns the same list with #skip(0)', () => { | |
| const list = Cons(4, () => Cons(3)); | |
| expect(list.skip(0)).to.eq(list); | |
| }); | |
| it('returns a list with the appropriate items', () => { | |
| const list = Cons(4, () => Cons(3)); | |
| expect(list.skip(1).toArray()).to.deep.eq([3]); | |
| expect(list.skip(2)).to.eq(Nil); | |
| expect(list.skip(3)).to.eq(Nil); | |
| }); | |
| }); | |
| describe('#skipUntil(predicate)', () => { | |
| it('returns the same list with false', () => { | |
| const list = Cons(4, () => Cons(3)); | |
| expect(list.skipUntil(() => true)).to.eq(list); | |
| }); | |
| it('returns a list with the appropriate items', () => { | |
| const list = Cons(4, () => Cons(3)); | |
| expect(list.skipUntil(() => false)).to.eq(Nil); | |
| expect(list.skipUntil(val => val === 3).toArray()).to.deep.eq([3]); | |
| }); | |
| }); | |
| describe('#skipUntil(predicate)', () => { | |
| it('returns the same list with true', () => { | |
| const list = Cons(4, () => Cons(3)); | |
| expect(list.skipUntil(() => true)).to.eq(list); | |
| }); | |
| it('returns a list with the appropriate items', () => { | |
| const list = Cons(4, () => Cons(3)); | |
| expect(list.skipUntil(() => false)).to.eq(Nil); | |
| expect(list.skipUntil(val => val === 3).toArray()).to.deep.eq([3]); | |
| }); | |
| }); | |
| describe('#skipWhile(predicate)', () => { | |
| it('returns the sameList with a false', () => { | |
| const list = Cons(4, () => Cons(3)); | |
| expect(list.skipWhile(() => false)).to.eq(list); | |
| }); | |
| it('returns a list with the appropriate items', () => { | |
| const list = Cons(4, () => Cons(3)); | |
| expect(list.skipWhile(() => true)).to.eq(Nil); | |
| expect(list.skipWhile(val => val !== 3).toArray()).to.deep.eq([3]); | |
| }); | |
| }); | |
| describe('#first(predicate)', () => { | |
| it('returns the first matching value', () => { | |
| const list = Cons(4, () => Cons(3)); | |
| expect(list.first(val => val === 4)).to.eq(4); | |
| expect(list.first(val => val === 3)).to.eq(3); | |
| }); | |
| }); | |
| describe('#concat(tailFn)', () => { | |
| it('returns a new list with the attached tail', () => { | |
| expect(Cons(4).concat(() => Cons(3)).toArray()).to.deep.eq([4, 3]); | |
| }); | |
| }); | |
| describe('#zip(List):List<array>', () => { | |
| it('returns Nil if Nil is passed in', () => { | |
| expect(Cons(4).zip(Nil)).to.eq(Nil); | |
| }); | |
| it('returns a new lazy evaluated list of pairs', () => { | |
| const tailExpr = () => { | |
| throw new Error('If this evaluated, lazy eval failed.'); | |
| }; | |
| expect(Cons(3, tailExpr).zip(Cons(4, tailExpr)).head).to.deep.eq([3, 4]); | |
| }); | |
| }); | |
| describe('#flatMap((val) => List):List', () => { | |
| it('creates a flat map of items', () => { | |
| const list = Cons(3, () => Cons(4)); | |
| expect(list.flatMap(() => Nil)).to.eq(Nil); | |
| expect(list.flatMap(val => Cons(val, () => Cons(val))).toArray()) | |
| .to.deep.eq([3, 3, 4, 4]); | |
| }); | |
| }); | |
| describe('#at(index)', () => { | |
| it('returns the appropriate item in a list', () => { | |
| const list = Cons(3, () => Cons(4)); | |
| expect(list.at(0)).to.eq(3); | |
| expect(list.at(1)).to.eq(4); | |
| expect(list.at(2)).to.be.undefined; | |
| }); | |
| }); | |
| describe('#forEach(action:(any, int) => void):any', () => { | |
| it('runs an action on each item in a list', () => { | |
| const items = []; | |
| const indices = []; | |
| Cons(3, () => Cons(4)).forEach((val, index) => { | |
| items.push(val); | |
| indices.push(index); | |
| }); | |
| expect(items).to.deep.eq([3, 4]); | |
| expect(indices).to.deep.eq([0, 1]); | |
| }); | |
| }); | |
| }); | |
| describe('#fromArray(array) => List', () => { | |
| it('returns Nil with an empty array', () => { | |
| expect(fromArray([])).to.eq(Nil); | |
| }); | |
| it('returns a List with the appropriate members', () => { | |
| expect(fromArray([0]).toArray()).to.deep.eq([0]); | |
| expect(fromArray([3, 4]).toArray()).to.deep.eq([3, 4]); | |
| }); | |
| }); | |
| describe('#stream(start:int = 0, incrementer:((any) => any) = val => val + 1)', () => { | |
| it('returns a list that starts with the start and increments according to the incrementer', () => { | |
| const streamBackFrom2 = stream(2, val => val - 1); | |
| expect(streamBackFrom2.head).to.eq(2); | |
| expect(streamBackFrom2.at(1)).to.eq(1); | |
| expect(streamBackFrom2.at(2)).to.eq(0); | |
| }); | |
| it('defaults the incrementer to val => val + 1', () => { | |
| const streamFrom2 = stream(2); | |
| expect(streamFrom2.head).to.eq(2); | |
| expect(streamFrom2.at(1)).to.eq(3); | |
| expect(streamFrom2.at(2)).to.eq(4); | |
| }); | |
| it('defaults the start to 0', () => { | |
| const streamFrom0 = stream(); | |
| expect(streamFrom0.head).to.eq(0); | |
| expect(streamFrom0.at(1)).to.eq(1); | |
| expect(streamFrom0.at(2)).to.eq(2); | |
| }); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment