scontainers
Version:
A container/collection/iterator library for JavaScript, comfortable to use, performant and versatile.
159 lines (117 loc) • 4.25 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 {
traits,
id
} = require('../utils.js');
_testTraitSet(traits.utils);
_testTraitSet(traits.scontainers);
_testTraitSet(traits.semantics);
const _describeScontainer = _getSymbol("describeScontainer", traits.utils, traits.scontainers, traits.semantics);
const _implCoreTraits = _getSymbol("implCoreTraits", traits.utils, traits.scontainers, traits.semantics);
const _map = _getSymbol("map", traits.utils, traits.scontainers, traits.semantics);
const _count = _getSymbol("count", traits.utils, traits.scontainers, traits.semantics);
const _sum = _getSymbol("sum", 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 _flatten = _getSymbol("flatten", traits.utils, traits.scontainers, traits.semantics);
module.exports = function (ParentCollection) {
return function () {
const parentProto = ParentCollection.prototype;
class Flatten {
static get name() {
return `${ParentCollection.name}::Flatten`;
}
constructor(coll) {
this.wrapped = coll;
}
toString() {
return `${this.wrapped}.flatten()`;
}
}
Flatten[_describeScontainer]({
InnerCollection: ParentCollection,
innerCollectionKey: id`wrapped`,
argKeys: []
});
Flatten[_implCoreTraits]({
count() {
// NOTE: might throw if one of the elements of the collections of `this.wrapped` isn't countable
if (parentProto[_map]) {
return function count() {
return this.wrapped[_map](value => value[_count] ? value[_count]() : 1)[_sum]();
};
}
},
kvIterator() {
return function kvIterator() {
// NOTE: might throw if one of the elements of the collections of `this.wrapped` isn't iterable
return {
it: this.wrapped[_kvIterator](),
// iterator on `this.wrapped`
jt: null,
// iterator on latest returned element of `this.wrapped`
next() {
for (;;) {
if (this.jt) {
const j = this.jt.next();
if (j) {
// found next element inside `jt`
return j;
} // reached the end of current `jt`
this.jt = null;
}
const i = this.it.next();
if (!i) {
// reached the end of `i`
return;
} // found `i`: next element inside `it`
if (i.value && i.value[_kvIterator]) {
this.jt = i.value[_kvIterator](); // `i` is iterable: iterating inside it
continue;
} // `i` was not iterable: just returning it
return i;
}
}
};
};
},
reverse() {
// NOTE: might throw if one of the elements of the collections of `this.wrapped` isn't reversable
if (parentProto[_reverse]) {
return function reverse() {
return this.wrapped[_reverse]()[_map](value => value[_kvIterator] ? value[_reverse]() : value)[_flatten]();
};
}
}
});
return Flatten;
};
};