Last active
January 3, 2018 03:03
-
-
Save hanmd82/605f30dd517dab11bb340287867650ed to your computer and use it in GitHub Desktop.
ES6 katas for Iterators - http://es6katas.org/
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
// The iterator protocol defines a standard way to produce a sequence of values (either finite or infinite). | |
// read more at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols | |
// 37: iterator/iterable - array. | |
describe('array is a built-in iterable object', function() { | |
const arr = ['a', 'B', 'see']; | |
describe('the iterator', function() { | |
it('an array has an iterator, which is a function', function() { | |
const iterator = arr[Symbol.iterator]; | |
const theType = typeof iterator; | |
const expected = 'function'; | |
assert.equal(theType, expected); | |
}); | |
it('can be looped with `for-of`, which expects an iterable', function() { | |
let count = 0; | |
for (let value of arr) { | |
count++; | |
} | |
assert.equal(count, arr.length); | |
}); | |
}); | |
describe('the iterator protocol', function() { | |
it('calling `next()` on an iterator returns an object according to the iterator protocol', function() { | |
const iterator = arr[Symbol.iterator](); | |
const firstItem = iterator.next(); | |
assert.deepEqual(firstItem, {done: false, value: 'a'}); | |
}); | |
it('the after-last element has done=true', function() { | |
const arr = []; | |
const iterator = arr[Symbol.iterator](); | |
const afterLast = iterator.next(); | |
assert.deepEqual(afterLast, {done: true, value: void 0}); | |
}); | |
}); | |
}); | |
// 38: iterator/iterable - string. | |
describe('string is a built-in iterable object', function() { | |
const s = 'abc'; | |
describe('string is iterable', function() { | |
it('the string`s object key `Symbol.iterator` is a function', function() { | |
const isA = typeof s[Symbol.iterator]; | |
assert.equal(isA, 'function'); | |
}); | |
it('use `Array.from()` to make an array out of any iterable', function(){ | |
const arr = Array.from(s); | |
assert.deepEqual(arr, ['a', 'b', 'c']); | |
}); | |
}); | |
describe('a string`s iterator', function() { | |
let iterator; | |
beforeEach(function() { | |
iterator = s[Symbol.iterator](); | |
}); | |
it('has a special string representation', function(){ | |
const description = iterator.toString(); | |
assert.equal(description, '[object String Iterator]'); | |
}); | |
it('`iterator.next()` returns an object according to the iterator protocol', function(){ | |
const value = iterator.next(); | |
assert.deepEqual(value, {done: false, value: 'a'}); | |
}); | |
it('the after-last call to `iterator.next()` says done=true, no more elements', function(){ | |
iterator.next(); | |
iterator.next(); | |
iterator.next(); | |
assert.equal(iterator.next().done, true); | |
}); | |
}); | |
}); | |
// 39: iterator - custom. Iterable is a protocol, when implemented allows objects | |
describe('A simple iterable without items inside, implementing the right protocol', () => { | |
function iteratorFunction() { | |
return { | |
next: () => { | |
return { done: true } | |
} | |
} | |
} | |
describe('the `iteratorFunction` needs to comply to the iterator protocol', function() { | |
it('must return an object', function() { | |
assert.equal(typeof iteratorFunction(), 'object'); | |
}); | |
it('the object must have a function assigned to a key `next`', function() { | |
assert.equal(typeof iteratorFunction().next, 'function'); | |
}); | |
it('calling `next()` must return an object with `{done: true}`', function() { | |
assert.deepEqual(iteratorFunction().next(), {done: true}); | |
}); | |
}); | |
let iterable; | |
beforeEach(function() { | |
iterable = { | |
[Symbol.iterator]: iteratorFunction | |
} | |
}); | |
describe('the iterable', function() { | |
it('must be an object', function() { | |
assert.equal(typeof iterable, 'object'); | |
}); | |
it('must have the iterator function assigned to the key `Symbol.iterator`', function() { | |
assert.equal(iterable[Symbol.iterator], iteratorFunction); | |
}); | |
}); | |
describe('using the iterable', function() { | |
it('it contains no values', function() { | |
let values = ''; | |
for (let value of iterable) { | |
values += value; | |
} | |
assert.equal(values, ''); | |
}); | |
it('has no `.length` property', function() { | |
const hasLengthProperty = typeof(iterable.length) !== 'undefined'; | |
assert.equal(hasLengthProperty, false); | |
}); | |
describe('can be converted to an array', function() { | |
it('using `Array.from()`', function() { | |
const arr = Array.from(iterable); | |
assert.equal(Array.isArray(arr), true); | |
}); | |
it('where `.length` is still 0', function() { | |
const arr = Array.from(iterable); | |
const length = arr.length; | |
assert.equal(length, 0); | |
}); | |
}); | |
}); | |
}); | |
// 40: iterator - one example usage. Build an iterable and use it with some built-in ES6 constructs. | |
// Consumable users: | |
// - `consumableUser` contains a consumable user, | |
// - `anyLeft` tells if there is any user left that can be consumed. | |
class ConsumableUsers { | |
constructor() { | |
this.users = ['Alice', 'Bob']; | |
this.isEmpty = false; | |
} | |
get nextUser() { | |
if (this.users.length > 0) { | |
return `user: ${this.users.shift()}`; | |
} | |
this.isEmpty = true; | |
return void 0; | |
} | |
get anyLeft() { | |
return !this.isEmpty; | |
} | |
} | |
describe('Iterator usages', () => { | |
let usersIterable; | |
beforeEach(function(){ | |
const consumableUsers = new ConsumableUsers(); | |
function iteratorFunction() { | |
return { | |
next: function() { | |
return {value: consumableUsers.nextUser, done: !consumableUsers.anyLeft} | |
} | |
} | |
} | |
usersIterable = { | |
[Symbol.iterator]: iteratorFunction | |
}; | |
}); | |
describe('create an iterator/iterable', function() { | |
it('the `usersIterable` should be iterable', function() { | |
const isIterable = Symbol.iterator in usersIterable; | |
assert.equal(isIterable, true); | |
}); | |
it('the iterator of `usersIterable` should return an object', function() { | |
const iterator = usersIterable[Symbol.iterator](); | |
assert.equal(typeof iterator, 'object'); | |
}); | |
it('the iterator of `usersIterable` should have a next function', function() { | |
const iterator = usersIterable[Symbol.iterator](); | |
assert.equal(typeof iterator.next, 'function'); | |
}); | |
}); | |
describe('fill the iterable with content using `ConsumableUsers`', function() { | |
describe('using the iterator', function() { | |
let iterator; | |
beforeEach(function(){ | |
iterator = usersIterable[Symbol.iterator](); | |
}); | |
it('should return `Alice` as first user', function() { | |
const firstItem = iterator.next(); | |
assert.deepEqual(firstItem, {value: "user: Alice", done: false}); | |
}); | |
it('should return `Bob` as second user', function() { | |
iterator.next(); // drop the first item | |
const secondItem = iterator.next(); | |
assert.deepEqual(secondItem, {value: "user: Bob", done: false}); | |
}); | |
it('should return `done:true`, which means there are no more items', function() { | |
iterator.next(); | |
iterator.next(); | |
const beyondLast = iterator.next(); | |
assert.deepEqual(beyondLast, {value: void 0, done: true}); | |
}) | |
}); | |
describe('using built-in constructs', function() { | |
it('use `Array.from()` to convert an iterable to an array', function() { | |
const users = Array.from(usersIterable); | |
assert.deepEqual(users, ['user: Alice', 'user: Bob']); | |
}); | |
it('use for-of to loop over an iterable', function() { | |
const users = []; | |
for (let user of usersIterable) users.push(user); | |
assert.deepEqual(users, ['user: Alice', 'user: Bob']); | |
}); | |
it('use the spread-operator to convert/add iterable to an array', function() { | |
const users = ['noname', ...usersIterable]; | |
assert.deepEqual(users, ['noname', 'user: Alice', 'user: Bob']); | |
}); | |
it('destructure an iterable like an array', function() { | |
const [firstUser, secondUser] = usersIterable; | |
assert.equal(firstUser, 'user: Alice'); | |
assert.equal(secondUser, 'user: Bob'); | |
}) | |
}); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment