UNPKG

@osmium/iterate

Version:

A powerful, type-safe iteration library for JavaScript and TypeScript with advanced mapping, parallel processing, and flow control features

522 lines 20.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Iterate = void 0; exports.iterateSync = iterateSync; exports.iterateAsync = iterateAsync; exports.iterate = iterate; exports.iterateKeysSync = iterateKeysSync; exports.iterateKeysAsync = iterateKeysAsync; exports.iterateKeys = iterateKeys; exports.iterateParallel = iterateParallel; exports.iterateKeysParallel = iterateKeysParallel; exports.seriesPageableRange = seriesPageableRange; exports.iterateParallelLimit = iterateParallelLimit; const is_1 = require("@osmium/is"); class Iterate { states; static createInstance() { return new Iterate(); } async iterateAsync(values, cb, map, mapUndefined, iterateKeys) { this.states = Iterate.States.createInstance({ isAsync: true, values, cb, map, mapUndefined, iterateKeys }); return this.resolveTypes(this.states.values, { isNulled: () => undefined, isArray: () => this.iterateArrayLikeAsync(), isSet: () => this.iterateArrayLikeAsync(), isMap: () => this.iterateMapAsync(), isNumber: () => this.iterateNumberAsync(), isTrue: () => this.iterateTrueAsync(), isString: () => this.iterateStringAsync(), isObject: () => this.iterateObjectAsync(), isUnknown: () => undefined }); } iterateSync(values, cb, map, mapUndefined, iterateKeys) { this.states = Iterate.States.createInstance({ isAsync: false, values, cb, map, mapUndefined, iterateKeys }); return this.resolveTypes(this.states.values, { isNulled: () => undefined, isArray: () => this.iterateArrayLikeSync(), isSet: () => this.iterateArrayLikeSync(), isMap: () => this.iterateMapSync(), isNumber: () => this.iterateNumberSync(), isTrue: () => this.iterateTrueSync(), isString: () => this.iterateStringSync(), isObject: () => this.iterateObjectSync(), isUnknown: () => undefined }); } valuesLength(values) { return this.resolveTypes(values, { isNulled: () => 0, isArray: (v) => v.length, isSet: (v) => v.size, isMap: (v) => v.size, isNumber: (v) => v, isTrue: () => 0, isString: (v) => v.length, isObject: (v) => Object.keys(v).length, isUnknown: () => 0 }); } getByIndex(index, values) { return this.resolveTypes(values, { isNulled: () => null, isArray: (v) => { if (index < 0 || index >= v.length) return null; return { idx: index, value: v[index] }; }, isSet: (v) => { const arr = [...v]; if (index < 0 || index >= arr.length) return null; return { idx: index, value: arr[index] }; }, isMap: (v) => { const keys = [...v.keys()]; if (index < 0 || index >= keys.length) return null; const idx = keys[index]; return { idx: idx, value: v.get(idx) }; }, isNumber: () => ({ idx: index, value: (index + 1) }), isTrue: () => null, isString: (v) => { if (index < 0 || index >= v.length) return null; return { idx: index, value: v[index] }; }, isObject: (v) => { const keys = Object.keys(v); if (index < 0 || index >= keys.length) return null; const idx = keys[index]; return { idx: idx, value: v[idx] }; }, isUnknown: () => null }); } resolveTypes(values, callbacks) { if ((0, is_1.isUndefined)(values) || (0, is_1.isNull)(values) || values === false || Number.isNaN(values)) return callbacks.isNulled(values); if ((0, is_1.isArray)(values)) return callbacks.isArray(values); if ((0, is_1.isSet)(values)) return callbacks.isSet(values); if ((0, is_1.isMap)(values)) return callbacks.isMap(values); if ((0, is_1.isNumeric)(values)) return callbacks.isNumber(values); if (values === true) return callbacks.isTrue(values); if ((0, is_1.isString)(values)) return callbacks.isString(values); if ((0, is_1.isObject)(values)) return callbacks.isObject(values); return callbacks.isUnknown(values); } iterateArrayLikeSync() { if (!this.states) throw new Error('States not initialized'); const rows = [...this.states.values.values()]; this.states.length = rows.length; for (this.states.position = 0; this.states.position < rows.length; this.states.position++) { Iterate.Row.processIterationSync(this.states, rows[this.states.position], this.states.position); if (this.states.break) return this.states.map; } return this.states.map; } async iterateArrayLikeAsync() { if (!this.states) throw new Error('States not initialized'); const rows = [...this.states.values.values()]; this.states.length = rows.length; for (this.states.position = 0; this.states.position < rows.length; this.states.position++) { await Iterate.Row.processIterationAsync(this.states, rows[this.states.position], this.states.position); if (this.states.break) return this.states.map; } return this.states.map; } iterateMapSync() { if (!this.states) throw new Error('States not initialized'); const values = [...this.states.values.entries()]; this.states.length = values.length; for (this.states.position = 0; this.states.position < values.length; this.states.position++) { Iterate.Row.processIterationSync(this.states, values[this.states.position][1], values[this.states.position][0]); if (this.states.break) return this.states.map; } return this.states.map; } async iterateMapAsync() { if (!this.states) throw new Error('States not initialized'); const values = [...this.states.values.entries()]; this.states.length = values.length; for (this.states.position = 0; this.states.position < values.length; this.states.position++) { await Iterate.Row.processIterationAsync(this.states, values[this.states.position][1], values[this.states.position][0]); if (this.states.break) return this.states.map; } return this.states.map; } iterateObjectSync() { if (!this.states) throw new Error('States not initialized'); if (this.states.values?.[Symbol.iterator]) { const valuesIter = [...[...this.states.values].entries()]; this.states.length = valuesIter.length; for (this.states.position = 0; this.states.position < valuesIter.length; this.states.position++) { Iterate.Row.processIterationSync(this.states, valuesIter[this.states.position][1], valuesIter[this.states.position][0]); if (this.states.break) return this.states.map; } return this.states.map; } const values = Object.entries(this.states.values); this.states.length = values.length; for (this.states.position = 0; this.states.position < values.length; this.states.position++) { Iterate.Row.processIterationSync(this.states, values[this.states.position][1], values[this.states.position][0]); if (this.states.break) return this.states.map; } return this.states.map; } async iterateObjectAsync() { if (!this.states) throw new Error('States not initialized'); if (this.states.values?.[Symbol.iterator]) { const valuesIter = [...[...this.states.values].entries()]; this.states.length = valuesIter.length; for (this.states.position = 0; this.states.position < valuesIter.length; this.states.position++) { await Iterate.Row.processIterationAsync(this.states, valuesIter[this.states.position][1], valuesIter[this.states.position][0]); if (this.states.break) return this.states.map; } return this.states.map; } const values = Object.entries(this.states.values); this.states.length = values.length; for (this.states.position = 0; this.states.position < values.length; this.states.position++) { await Iterate.Row.processIterationAsync(this.states, values[this.states.position][1], values[this.states.position][0]); if (this.states.break) return this.states.map; } return this.states.map; } iterateNumberSync() { if (!this.states) throw new Error('States not initialized'); const values = this.states.values; this.states.length = values + 1; for (this.states.position = 0; this.states.position < values; this.states.position++) { Iterate.Row.processIterationSync(this.states, this.states.position + 1, this.states.position); if (this.states.break) return this.states.map; } return this.states.map; } async iterateNumberAsync() { if (!this.states) throw new Error('States not initialized'); const values = this.states.values; this.states.length = values + 1; for (this.states.position = 0; this.states.position < values; this.states.position++) { await Iterate.Row.processIterationAsync(this.states, this.states.position + 1, this.states.position); if (this.states.break) return this.states.map; } return this.states.map; } iterateStringSync() { if (!this.states) throw new Error('States not initialized'); const values = this.states.values; this.states.length = values.length; for (this.states.position = 0; this.states.position < this.states.length; this.states.position++) { Iterate.Row.processIterationSync(this.states, values[this.states.position], this.states.position); if (this.states.break) return this.states.map; } return this.states.map; } async iterateStringAsync() { if (!this.states) throw new Error('States not initialized'); const values = this.states.values; this.states.length = values.length; for (this.states.position = 0; this.states.position < this.states.length; this.states.position++) { await Iterate.Row.processIterationAsync(this.states, values[this.states.position], this.states.position); if (this.states.break) return this.states.map; } return this.states.map; } iterateTrueSync() { if (!this.states) throw new Error('States not initialized'); let cnt = 0; this.states.length = Infinity; while (true) { this.states.position = cnt; Iterate.Row.processIterationSync(this.states, cnt + 1, cnt); if (this.states.break) return this.states.map; cnt++; // Safety check to prevent infinite loops without break condition if (cnt > Number.MAX_SAFE_INTEGER - 1) { throw new Error('Infinite iteration detected - no break condition found'); } } } async iterateTrueAsync() { if (!this.states) throw new Error('States not initialized'); let cnt = 0; this.states.length = Infinity; while (true) { this.states.position = cnt; await Iterate.Row.processIterationAsync(this.states, cnt + 1, cnt); if (this.states.break) return this.states.map; cnt++; // Safety check to prevent infinite loops without break condition if (cnt > Number.MAX_SAFE_INTEGER - 1) { throw new Error('Infinite iteration detected - no break condition found'); } } } } exports.Iterate = Iterate; (function (Iterate) { class Controller { static createInstance(row) { return new Controller(row); } row; constructor(row) { this.row = row; } get length() { return this.row.states.length; } break() { this.row.states.break = true; } repeat() { this.row.states.position--; } skip() { this.row.states.position++; } shift(n) { if (this.row.states.position + n < -1) n = -(this.row.states.position + 1); this.row.states.position += n; } get mapKey() { return this.row.states.map ? this.row.mapKey : undefined; } set mapKey(key) { if (!this.row.states.map) return; this.row.mapKey = key; } key(key) { this.mapKey = key; } getStates() { return this.row.states; } } Iterate.Controller = Controller; class Row { static createInstance(states, index) { return new Row(states, index); } states; mapKey; index; constructor(states, index) { this.states = states; this.index = index; if (Array.isArray(this.states.map)) { this.mapKey = this.states.position; return; } this.mapKey = index; } processMapper(mapperValue) { if (!this.states.mapUndefined && mapperValue === undefined) return false; if (Array.isArray(this.states.map)) { if (this.mapKey === this.states.position || !Number.isInteger(this.mapKey)) { this.states.map.push(mapperValue); } else { this.states.map[this.mapKey] = mapperValue; } return true; } if (this.states.map instanceof Set) { this.states.map.add(mapperValue); return true; } if (this.states.map instanceof Map) { this.states.map.set(this.mapKey, mapperValue); return true; } if (typeof this.states.map === 'number') { this.states.map++; return true; } if (typeof this.states.map === 'boolean' && !this.states.mapChanged) { this.states.map = !this.states.map; return true; } if (typeof this.states.map === 'object') { this.states.map[this.mapKey] = mapperValue; return true; } return false; } static processIterationSync(states, value, index) { const instance = Row.createInstance(states, index); const mapperValue = states.iterateKeys ? states.cb(index, value, Controller.createInstance(instance)) : states.cb(value, index, Controller.createInstance(instance)); instance.processMapper(mapperValue); } static async processIterationAsync(states, value, index) { const instance = Row.createInstance(states, index); const mapperValue = states.iterateKeys ? await states.cb(index, value, Controller.createInstance(instance)) : await states.cb(value, index, Controller.createInstance(instance)); instance.processMapper(mapperValue); } } Iterate.Row = Row; class States { static createInstance(args) { const instance = new States(); Object.assign(instance, args); return instance; } isAsync = false; values; cb; map; mapUndefined = false; mapChanged = false; iterateKeys = false; length = 0; position = 0; break = false; getStates() { return this; } } Iterate.States = States; })(Iterate || (exports.Iterate = Iterate = {})); function iterateSync(values, cb, map, mapUndefined) { return Iterate.createInstance().iterateSync(values, cb, map, mapUndefined, false); } function iterateAsync(values, cb, map, mapUndefined) { return Iterate.createInstance().iterateAsync(values, cb, map, mapUndefined, false); } function iterate(values, cb, map, mapUndefined) { if ((0, is_1.isAsyncFunction)(cb)) { return Iterate.createInstance().iterateAsync(values, cb, map, mapUndefined, false); } return Iterate.createInstance().iterateSync(values, cb, map, mapUndefined, false); } function iterateKeysSync(values, cb, map, mapUndefined) { return Iterate.createInstance().iterateSync(values, cb, map, mapUndefined, true); } function iterateKeysAsync(values, cb, map, mapUndefined) { return Iterate.createInstance().iterateAsync(values, cb, map, mapUndefined, true); } function iterateKeys(values, cb, map, mapUndefined) { if ((0, is_1.isAsyncFunction)(cb)) { return Iterate.createInstance().iterateAsync(values, cb, map, mapUndefined, true); } return Iterate.createInstance().iterateSync(values, cb, map, mapUndefined, true); } async function iterateParallel(values, cb, map, mapUndefined, iterateKeys = false) { const states = {}; let idCnt = 0; const promises = iterateSync(values, (val, idx, iter) => new Promise(async (resolve) => { const cid = idCnt; idCnt++; const origKey = iter.key; const ret = iterateKeys ? await cb(idx, val, iter) : await cb(val, idx, iter); states[cid] = { val: ret, key: iter.mapKey }; iter.mapKey = origKey; resolve(cid); }), []); await Promise.all(promises); return iterateSync(states, ((val, idx, iter) => { iter.key(val.key); return val.val; }), map, mapUndefined); } async function iterateKeysParallel(values, cb, map, mapUndefined) { return iterateParallel(values, cb, map, mapUndefined, true); } function seriesPageableRange(start, end, inPage) { const delta = end - start; const count = Math.ceil(delta / inPage); return iterateKeysSync(count, (idx, _, iter) => { const from = start + (inPage * idx); if (from + inPage >= end) { if (from + inPage === end) { iter.getStates().map.push([from, end - 1]); return [end, end]; } return [from, end]; } return [from, from + inPage - 1]; }, []); } async function iterateParallelLimit(limit, values, cb, iterateKeys = false) { const instance = Iterate.createInstance(); const length = instance.valuesLength(values); if (!length) return; const batchCount = Math.ceil(length / limit); for (let batchIdx = 0; batchIdx < batchCount; batchIdx++) { const pr = iterateSync(limit, (_, key, iter) => { const cursor = (batchIdx * limit) + key; if (cursor >= length) return iter.break(); const param = instance.getByIndex(cursor, values); if (!param) return; return (async () => iterateKeys ? cb(param.idx, param.value, iter) : cb(param.value, param.idx, iter))(); }, []); await Promise.all(pr); } } //# sourceMappingURL=index.js.map