scontainers
Version:
A container/collection/iterator library for JavaScript, comfortable to use, performant and versatile.
325 lines (219 loc) • 7.65 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 straits = require('straits');
_testTraitSet(straits.core);
const _assign = _getSymbol("assign", straits.core);
// core traits
const coreTraits = straits.utils.TraitSet.fromKeys({
// *** static methods
from(collection) {},
// return a new instance of the collection constructed from `collection`
// naturally-indexed collections (e.g. Array, Range)
nth(n) {},
// return the ${n}-th element - O(1)
nthKVN(n) {},
// NOTE: it doesn't check to see if `n` is there - O(1)
setNth(n, value) {},
// sets the ${n}-th element to ${value} - O(1)
truncate(n) {},
// shortens ${this} to contain the first ${n} elements only, discarding the others - O(1)
// needed to make naturally-indexed collections compatible with the associative collection API:
nToKey(n) {},
// return the key associated to the ${n}-th element (${get( nToKey(n) ) === nth(n)})
keyToN(key) {},
// return the index associated to the element with key ${key} (${get(key) === nth(keyToN(key))})
// associative collections (e.g. Map)
// note that naturally-indexed collections are also associative, thanks to `nToKey` and `keyToN`
get(key) {},
// return the value of key ${key} - O(1)
getKVN(key) {},
// NOTE: it doesn't check to see if ${key} is there - O(1)
set(key, value) {},
// set ${value} as value for ${key} - O(1)
hasKey(key) {},
// return true if ${this} has a key ${key}, false otherwise - O(1)
deleteKey(key) {},
// remove the item with key ${key} from ${this}
// unindexed collections (e.g. Set, a list)
has(item) {},
// return true or false depending on whether ${item} is in ${this} - O(1)
delete(item) {},
// removes ${item} from ${this} - O(1)
// collections without a key, or with automatic key (e.g. Set, naturally-indexed collections)
add(value) {},
// adds ${value} to ${this} - O(1)
// collections with known size
len() {},
// return the number of elements in the collection - O(1)
// reversible collections
reverse() {},
// reverse iteration order - O(1)
reserve(n) {},
// makes ${this} ready to host a total of ${n} items - O(1)
clear() {},
// remove every item from ${this} - O(1)
// iterable collections
//iterator(){}, // not redefining it: reusing `[Symbol.Iterator]`
kvIterator() {},
// identical to `[Symbol.Iterator]`, except the returned value is always in the form [key, value]
kvReorderedIterator() {},
// kvAsyncIterator(){}, // TODO: hmmmm...
inplace() {}
}); // automatically implemented traits
const derivedTraits = straits.utils.TraitSet.fromKeys({
// madifying functions
// TODO: shouldn't these be core? they need to make assumptions on how to grow e.g. Array
push(value) {},
// add ${value} to the end of ${this} - O(1)
unshift(value) {},
// add ${value} to the beginning of ${this} - O(1)
pop() {},
// remove the element at the end of ${this} - O(1)
shift() {},
// remove the element at the beginning of ${this} - O(1)
insert(n, value) {},
// insert ${value} after the ${n}-th element of ${this}
remove(n) {},
// remove the ${n}-th element of ${this}, shifting all the following ones
// iterating functions
forEach(fn) {},
// call ${fn(value, key)} for every item in ${this} - O(n)
whileEach(fn) {},
// - O(n)
untilEach(fn) {},
// - O(n)
// length information functions
count() {},
// return the number of elements in the collection - any cost, often O(n)
isEmpty() {},
// return true if the collection is empty, false otherwise - O(1)
// special access functions - they all return KVNs
only() {},
// return the only item in ${this} - O(1)
first() {},
// return the first item in ${this} - O(1)
last() {},
// return the last item in ${this} - O(1)
random() {},
// return a random item in ${this} - O(n) // TODO: or O(1) ?
find(fn) {},
// returning a KV
findLast(fn) {},
// returning a KV
swapNs(n1, n2) {},
// swap two elements (by n)
swapKeys(k1, k2) {},
// swap two elements (by key)
reduce(fn, initialValue) {},
reduceFirst(fn) {},
// reductions
sum() {},
avg() {},
min() {},
max() {},
join(sep = '') {},
// returns a string
every(fn) {},
some(fn) {},
sort(TargetArrayType = Array) {},
shuffle() {},
permute() {},
collect(TargetType) {},
// creates a new ${TargetType} made of the content of ${this}
consume(TargetType) {},
// like ${collect}, but might modify (and even return) ${this}
// TODO: the following still need to be implemented
remap(fn) {},
// [k:v]::remap( fn(v,k)->(k':v') ) -> [k':v']
kvMap(fn) {},
// [k:v]::kvMap( fn(v,k)->v' ) -> [(k,v):v'] // ._Straits.mapKey( (v,k)=>{k,v} )._Straits.map( (v,{k})=>fn(v,k) )
unmap(fn) {},
// [(k,v)]::unmap() -> [k:v]
unmapKeys(fn) {},
// ._Straits.keys()._Straits.unmap()
allProperties() {},
// not iterable - manages all properties, owned or not, enumerable or not
collectInto(target) {},
repeat(n) {}
});
const decoratorTraits = straits.utils.TraitSet.fromKeys({
auto() {},
// after this, any operation will be available regardless of supported traits and complexity - if some are not supported, we automatically `collect` into a working type.
keys() {},
// mostly needed because of `iterator`: iterates on a single value, rather than a KV
values() {},
// mostly needed because of `iterator`: iterates on a single value, rather than a KV
entries() {},
// mostly needed because of `iterator`: iterates on a KV
properties() {},
// iterable: it's the enumerable properties
ownProperties() {},
// the own properties of `this`
enumerate() {},
// map( value => {count, value} )
filter(fn) {},
uniq() {},
// removes consecutive duplicates
slice(begin, end) {},
chunk(n) {},
map(fn) {},
mapKey() {},
cache(CacheType) {},
// cache values resulting from `get()`, `nth()` etc. NOTE: it doesn't cache iteration!
iter() {},
// removes other properties, leaves kvIterator only
reordered() {},
// removes other properties, leaves kvReorderedIterator only
cow(CopyType) {},
// copy on write: collects() as soon as you try to modify it
groupBy(fn) {},
flatten() {},
flattenDeep() {},
concat(...collections) {},
skipWhile(fn) {},
takeWhile(fn) {},
skip(n) {},
take(n) {},
assign(...collections) {},
defaults(...collections) {},
groupWhile() {}
});
derivedTraits[_assign]({
iterator: Symbol.iterator,
toString: straits.common.toString
});
const traits = {}[_assign](derivedTraits, coreTraits, decoratorTraits);
traits.coreTraits = coreTraits;
traits.derivedTraits = derivedTraits;
traits.decoratorTraits = decoratorTraits;
module.exports = traits;
if (require.main === module) {
console.log(Object.keys(traits).join(`, `));
}