scontainers
Version:
A container/collection/iterator library for JavaScript, comfortable to use, performant and versatile.
179 lines (137 loc) • 4.56 kB
JavaScript
;
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;
};
};