UNPKG

scontainers

Version:

A container/collection/iterator library for JavaScript, comfortable to use, performant and versatile.

179 lines (137 loc) 4.56 kB
"use strict"; function _implSymbol(target, sym, value) { Object.defineProperty(target, sym, { value, configurable: true }); return target[sym]; } function _getSymbol(targetSymName, ...traitSets) { let symbol; traitSets.forEach(traitSet => { const sym = traitSet[targetSymName]; if (typeof sym === 'symbol') { if (!!symbol && symbol !== sym) { throw new Error(`Symbol ${targetSymName} offered by multiple trait sets.`); } symbol = sym; } }); if (!symbol) { throw new Error(`No trait set is providing symbol ${targetSymName}.`); } return symbol; } function _testTraitSet(traitSet) { if (!traitSet || typeof traitSet === 'boolean' || typeof traitSet === 'number' || typeof traitSet === 'string') { throw new Error(`${traitSet} cannot be used as a trait set.`); } } const { assert, traits, id, KVN } = require('../utils.js'); _testTraitSet(traits.utils); _testTraitSet(traits.scontainers); _testTraitSet(traits.semantics); const _whileEach = _getSymbol("whileEach", traits.utils, traits.scontainers, traits.semantics); const _describeScontainer = _getSymbol("describeScontainer", traits.utils, traits.scontainers, traits.semantics); const _implCoreTraits = _getSymbol("implCoreTraits", traits.utils, traits.scontainers, traits.semantics); const _len = _getSymbol("len", traits.utils, traits.scontainers, traits.semantics); const _nth = _getSymbol("nth", traits.utils, traits.scontainers, traits.semantics); const _slice = _getSymbol("slice", traits.utils, traits.scontainers, traits.semantics); const _kvIterator = _getSymbol("kvIterator", traits.utils, traits.scontainers, traits.semantics); const _reverse = _getSymbol("reverse", traits.utils, traits.scontainers, traits.semantics); const _concat = _getSymbol("concat", traits.utils, traits.scontainers, traits.semantics); const _map = _getSymbol("map", traits.utils, traits.scontainers, traits.semantics); module.exports = function (ParentCollection) { const parentProto = ParentCollection.prototype; if (!parentProto[_whileEach]) { return; } return function () { class Chunk { static get name() { return `${ParentCollection.name}::Chunk`; } constructor(coll, n) { this.wrapped = coll; this.n = n; assert(this.n > 0, `Invalid parameter for chunk()`); } toString() { return `${this.wrapped}.chunk(${this.n})`; } } Chunk[_describeScontainer]({ InnerCollection: ParentCollection, innerCollectionKey: id`wrapped`, argKeys: [id`n`] }); Chunk[_implCoreTraits]({ len() { if (parentProto[_len]) { return function len() { return Math.ceil(this.wrapped[_len]() / this.n); }; } }, nth() { if (parentProto[_nth] && parentProto[_len]) { return function (n) { return this.wrapped[_slice](n * this.n, Math.min(this.wrapped[_len](), (n + 1) * this.n)); }; } }, kvIterator() { // TODO: don't allocate a `Map`: use a reordered iterator // TODO: if you can clone the iterator on `this.wrapped`, do it! return function kvIterator() { return { n: this.n, count: 0, it: this.wrapped[_kvIterator](), next() { const chunk = new Map(); for (let i = 0; i < this.n; ++i) { const next = this.it.next(); if (!next) { break; } const { key, value, n } = next; chunk.set(key, value, n); } if (chunk.size) { return new KVN(this.count++, chunk); } } }; }; }, reverse() { if (parentProto[_reverse] && parentProto[_len]) { return function reverse() { const remainder = this.wrapped[_len]() % this.n; if (remainder === 0) { return new Chunk(this.wrapped[_reverse](), this.n); } else { return new Chunk(new Array(remainder)[_concat](this.wrapped[_reverse]())[_map]((chunk, i) => { if (i === 0) { return chunk.skip(remainder); } return chunk; }), this.n); } }; } } }); return Chunk; }; };