@onekuma/seq
Version:
OneKuma Sequence Collection
304 lines (301 loc) • 6.32 kB
JavaScript
import { Option } from '@onekuma/option';
class BaseSeq {
}
class Seq extends BaseSeq {
constructor(forEach, done = false) {
super();
this.forEach = forEach;
this.done = done;
}
static of(...args) {
return this.from(args);
}
static from(arr) {
if (Array.isArray(arr)) {
let i = 0;
return new Seq((consume) => {
if (i < arr.length) {
consume(arr[i++]);
return true;
} else {
return false;
}
});
} else {
return new Seq((consume) => {
const cur = arr.next();
if (!cur.done) {
consume(cur.value);
return true;
} else {
return false;
}
});
}
}
static range(...args) {
if (args.length === 1 || args.length === 2) {
const n = args[args.length - 1];
let i = args.length === 1 ? 0 : args[0];
return new Seq((consume) => {
if (i < n) {
consume(i++);
return true;
} else {
return false;
}
});
} else {
return Seq.of();
}
}
map(fn) {
return new Seq((consume) => this.forEach((t) => consume(fn(t))));
}
enumerate() {
let i = 0;
return this.map((e) => [i++, e]);
}
filter(predicate) {
return new Seq((consume) => this.forEach((t) => predicate(t) && consume(t)));
}
filterDef() {
return this.filter((e) => e !== void 0 && e !== null);
}
catch(handler) {
return new Seq((consume) => {
try {
return this.forEach((t) => consume(t));
} catch (error) {
handler && handler(error);
return true;
}
});
}
first() {
let elem = Option.none();
this.forEach((t) => elem = Option.some(t));
return elem;
}
last() {
let elem = Option.none();
while (this.forEach((t) => elem = Option.some(t)))
;
return elem;
}
take(count) {
let i = 0;
return new Seq((consume) => {
if (i < count) {
i++;
return this.forEach((t) => consume(t));
} else {
return false;
}
});
}
drop(count) {
let i = 0;
return new Seq(
(consume) => this.forEach((t) => {
if (i < count) {
i++;
} else {
consume(t);
}
})
);
}
static chain(...args) {
if (args.length > 0) {
return args[0].concat(...args.slice(1));
} else {
return Seq.of();
}
}
concat(...args) {
let i = 0;
return new Seq((consume) => {
while (this.forEach((t) => consume(t)))
;
while (i < args.length) {
let cont = args[i].forEach((t) => consume(t));
if (cont) {
return true;
}
i++;
}
return false;
});
}
append(...args) {
return this.concat(Seq.of(...args));
}
*[Symbol.iterator]() {
let elem = void 0;
while (this.forEach((t) => elem = t)) {
yield elem;
}
}
async() {
const it = this[Symbol.iterator]();
return new AsyncSeq(async (consume) => {
const cur = it.next();
if (!cur.done) {
await consume(cur.value);
return true;
} else {
return false;
}
}, this.done);
}
// API that closees this sequence
reduce(reducer) {
this.close();
let f = this.first();
if (f.isNone)
return f;
let acc = f.value;
while (this.forEach((t) => acc = reducer(acc, t)))
;
return Option.some(acc);
}
fold(initial, reducer) {
this.close();
let acc = initial;
while (this.forEach((t) => acc = reducer(acc, t)))
;
return acc;
}
async parallel(fn) {
const arr = this.toArray();
return await Promise.all(arr.map((el) => fn(el)));
}
toArray() {
this.close();
const arr = [];
while (this.forEach((t) => arr.push(t)))
;
return arr;
}
close() {
if (this.done) {
throw new SeqError();
}
this.done = true;
}
}
class AsyncSeq extends BaseSeq {
constructor(forEach, done = false) {
super();
this.forEach = forEach;
this.done = done;
}
static of(...args) {
return this.from(args);
}
static from(arr) {
return Seq.from(arr).async();
}
map(fn) {
return new AsyncSeq(
async (consume) => await this.forEach(async (t) => await consume(await fn(t)))
);
}
filter(fn) {
return new AsyncSeq(
async (consume) => await this.forEach(async (t) => {
if (await fn(t)) {
await consume(t);
}
})
);
}
// API that closees this sequence
async toArray() {
this.close();
const arr = [];
while (await this.forEach(async (t) => {
arr.push(t);
}))
;
return arr;
}
close() {
if (this.done) {
throw new SeqError();
}
this.done = true;
}
}
class SeqError extends Error {
constructor() {
super("This seq has been consumed");
}
}
function range(...args) {
if (args.length === 1) {
const n = Array.isArray(args[0]) ? args[0].length : args[0];
let i = 0;
return new Seq((consume) => {
if (i < n) {
consume(i++);
return true;
} else {
return false;
}
});
} else if (args.length === 2) {
let i = args[0];
const n = args[1];
if (args[0] <= args[1]) {
return new Seq((consume) => {
if (i < n) {
consume(i++);
return true;
} else {
return false;
}
});
} else {
return new Seq((consume) => {
if (i > n) {
consume(i--);
return true;
} else {
return false;
}
});
}
} else if (args.length === 3) {
let i = args[0];
const n = args[1];
const step = args[2];
if (args[0] < args[1]) {
return new Seq((consume) => {
if (i < n) {
consume(i);
i += step;
return true;
} else {
return false;
}
});
} else if (args[0] > args[1]) {
return new Seq((consume) => {
if (i > n) {
consume(i);
i -= step;
return true;
} else {
return false;
}
});
} else {
return Seq.of();
}
} else {
return Seq.of();
}
}
export { AsyncSeq, Seq, SeqError, range };