@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
JavaScript
"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