@byloth/core
Version:
An unopinionated collection of useful functions and classes that I use widely in all my projects. 🔧
1,647 lines • 170 kB
JavaScript
const T = typeof window < "u" && typeof window.document < "u", D = typeof process < "u" && !!process.versions?.node, B = typeof self == "object" && self.constructor?.name === "DedicatedWorkerGlobalScope";
class c extends Error {
/**
* A static method to convert a generic caught error, ensuring it's an instance of the {@link Exception} class.
*
* ---
*
* @example
* ```ts
* try { [...] }
* catch (error)
* {
* const exc = Exception.FromUnknown(error);
*
* [...]
* }
* ```
*
* ---
*
* @param error The caught error to convert.
*
* @returns An instance of the {@link Exception} class.
*/
static FromUnknown(e) {
if (e instanceof c)
return e;
if (e instanceof Error) {
const t = new c(e.message);
return t.stack = e.stack, t.cause = e.cause, t.name = e.name, t;
}
return new c(`${e}`);
}
/**
* Initializes a new instance of the {@link Exception} class.
*
* ---
*
* @example
* ```ts
* throw new Exception("An error occurred while processing the request.");
* ```
*
* ---
*
* @param message The message that describes the error.
* @param cause The previous caught error that caused this one, if any.
* @param name The name of the exception. Default is `"Exception"`.
*/
constructor(e, t, n = "Exception") {
super(e), this.cause = t, this.name = n;
}
[Symbol.toStringTag] = "Exception";
}
class x extends c {
/**
* Initializes a new instance of the {@link FatalErrorException} class.
*
* ---
*
* @example
* ```ts
* throw new FatalErrorException("This error should never happen. Please, contact the support team.");
* ```
*
* ---
*
* @param message The message that describes the error.
* @param cause The previous caught error that caused this one, if any.
* @param name The name of the exception. Default is `"FatalErrorException"`.
*/
constructor(e, t, n = "FatalErrorException") {
e === void 0 && (e = "The program has encountered an unrecoverable error and cannot continue as expected. Please, try again later. If the problem persists, contact the support team."), super(e, t, n);
}
[Symbol.toStringTag] = "FatalErrorException";
}
class P extends x {
/**
* Initializes a new instance of the {@link NotImplementedException} class.
*
* ---
*
* @example
* ```ts
* throw new NotImplementedException("This method hasn't been implemented yet. Check back later.");
* ```
*
* ---
*
* @param message The message that describes the error.
* @param cause The previous caught error that caused this one, if any.
* @param name The name of the exception. Default is `"NotImplementedException"`.
*/
constructor(e, t, n = "NotImplementedException") {
e === void 0 && (e = "This feature isn't implemented yet. Please, try again later."), super(e, t, n);
}
[Symbol.toStringTag] = "NotImplementedException";
}
class E extends c {
/**
* Initializes a new instance of the {@link FileException} class.
*
* ---
*
* @example
* ```ts
* throw new FileException("An error occurred while trying to read the file.");
* ```
*
* ---
*
* @param message The message that describes the error.
* @param cause The previous caught error that caused this one, if any.
* @param name The name of the exception. Default is `"FileException"`.
*/
constructor(e, t, n = "FileException") {
super(e, t, n);
}
[Symbol.toStringTag] = "FileException";
}
class J extends E {
/**
* Initializes a new instance of the {@link FileExistsException} class.
*
* ---
*
* @example
* ```ts
* throw new FileExistsException("The file named 'data.json' already exists on the server.");
* ```
*
* ---
*
* @param message The message that describes the error.
* @param cause The previous caught error that caused this one, if any.
* @param name The name of the exception. Default is `"FileExistsException"`.
*/
constructor(e, t, n = "FileExistsException") {
super(e, t, n);
}
[Symbol.toStringTag] = "FileExistsException";
}
class Y extends E {
/**
* Initializes a new instance of the {@link FileNotFoundException} class.
*
* ---
*
* @example
* ```ts
* throw new FileNotFoundException("The file named 'data.json' wasn't found on the server.");
* ```
*
* ---
*
* @param message The message that describes the error.
* @param cause The previous caught error that caused this one, if any.
* @param name The name of the exception. Default is `"FileNotFoundException"`.
*/
constructor(e, t, n = "FileNotFoundException") {
super(e, t, n);
}
[Symbol.toStringTag] = "FileNotFoundException";
}
class p extends c {
/**
* Initializes a new instance of the {@link KeyException} class.
*
* ---
*
* @example
* ```ts
* throw new KeyException("The 'id' key wasn't found in the dictionary.");
* ```
*
* ---
*
* @param message The message that describes the error.
* @param cause The previous caught error that caused this one, if any.
* @param name The name of the exception. Default is `"KeyException"`.
*/
constructor(e, t, n = "KeyException") {
super(e, t, n);
}
[Symbol.toStringTag] = "KeyException";
}
class L extends c {
/**
* Initializes a new instance of the {@link NetworkException} class.
*
* ---
*
* @example
* ```ts
* throw new NetworkException("Couldn't connect to the server. Please, try again later.");
* ```
*
* ---
*
* @param message The message that describes the error.
* @param cause The previous caught error that caused this one, if any.
* @param name The name of the exception. Default is `"NetworkException"`.
*/
constructor(e, t, n = "NetworkException") {
super(e, t, n);
}
[Symbol.toStringTag] = "NetworkException";
}
class V extends c {
/**
* Initializes a new instance of the {@link PermissionException} class.
*
* ---
*
* @example
* ```ts
* throw new PermissionException("You don't have permission to access this resource.");
* ```
*
* ---
*
* @param message The message that describes the error.
* @param cause The previous caught error that caused this one, if any.
* @param name The name of the exception. Default is `"PermissionException"`.
*/
constructor(e, t, n = "PermissionException") {
super(e, t, n);
}
[Symbol.toStringTag] = "PermissionException";
}
class k extends c {
/**
* Initializes a new instance of the {@link ReferenceException} class.
*
* ---
*
* @example
* ```ts
* throw new ReferenceException("The 'canvas' element wasn't found in the document.");
* ```
*
* ---
*
* @param message The message that describes the error.
* @param cause The previous caught error that caused this one, if any.
* @param name The name of the exception. Default is `"ReferenceException"`.
*/
constructor(e, t, n = "ReferenceException") {
super(e, t, n);
}
[Symbol.toStringTag] = "ReferenceException";
}
class _ extends c {
/**
* Initializes a new instance of the {@link RuntimeException} class.
*
* ---
*
* @example
* ```ts
* throw new RuntimeException("The received input seems to be malformed or corrupted.");
* ```
*
* ---
*
* @param message The message that describes the error.
* @param cause The previous caught error that caused this one, if any.
* @param name The name of the exception. Default is `"RuntimeException"`.
*/
constructor(e, t, n = "RuntimeException") {
super(e, t, n);
}
[Symbol.toStringTag] = "RuntimeException";
}
class j extends _ {
/**
* Initializes a new instance of the {@link EnvironmentException} class.
*
* ---
*
* @example
* ```ts
* throw new EnvironmentException("The required environment variable 'API_KEY' isn't set.");
* ```
*
* ---
*
* @param message The message that describes the error.
* @param cause The previous caught error that caused this one, if any.
* @param name The name of the exception. Default is `"EnvironmentException"`.
*/
constructor(e, t, n = "EnvironmentException") {
super(e, t, n);
}
[Symbol.toStringTag] = "EnvironmentException";
}
class O extends c {
/**
* Initializes a new instance of the {@link TimeoutException} class.
*
* ---
*
* @example
* ```ts
* throw new TimeoutException("The task took too long to complete.");
* ```
*
* ---
*
* @param message The message that describes the error.
* @param cause The previous caught error that caused this one, if any.
* @param name The name of the exception. Default is `"TimeoutException"`.
*/
constructor(e, t, n = "TimeoutException") {
super(e, t, n);
}
[Symbol.toStringTag] = "TimeoutException";
}
class G extends c {
/**
* Initializes a new instance of the {@link TypeException} class.
*
* ---
*
* @example
* ```ts
* throw new TypeException("The 'username' argument must be a valid string.");
* ```
*
* ---
*
* @param message The message that describes the error.
* @param cause The previous caught error that caused this one, if any.
* @param name The name of the exception. Default is `"TypeException"`.
*/
constructor(e, t, n = "TypeException") {
super(e, t, n);
}
[Symbol.toStringTag] = "TypeException";
}
class f extends c {
/**
* Initializes a new instance of the {@link ValueException} class.
*
* ---
*
* @example
* ```ts
* throw new ValueException("The 'grade' argument cannot be negative.");
* ```
*
* ---
*
* @param message The message that describes the error.
* @param cause The previous caught error that caused this one, if any.
* @param name The name of the exception. Default is `"ValueException"`.
*/
constructor(e, t, n = "ValueException") {
super(e, t, n);
}
[Symbol.toStringTag] = "ValueException";
}
class b extends f {
/**
* Initializes a new instance of the {@link RangeException} class.
*
* ---
*
* @example
* ```ts
* throw new RangeException("The 'percentage' argument must be between 0 and 100.");
* ```
*
* ---
*
* @param message The message that describes the error.
* @param cause The previous caught error that caused this one, if any.
* @param name The name of the exception. Default is `"RangeException"`.
*/
constructor(e, t, n = "RangeException") {
super(e, t, n);
}
[Symbol.toStringTag] = "RangeException";
}
class l {
/**
* The native {@link Iterator} object that is being wrapped by this instance.
*/
_iterator;
constructor(e) {
e instanceof Function ? this._iterator = e() : Symbol.iterator in e ? this._iterator = e[Symbol.iterator]() : this._iterator = e;
}
/**
* Determines whether all elements of the iterator satisfy a given condition.
* See also {@link SmartIterator.some}.
*
* This method will iterate over all elements of the iterator checking if they satisfy the condition.
* Once a single element doesn't satisfy the condition, the method will return `false` immediately.
*
* This may lead to an unknown final state of the iterator, which may be entirely or partially consumed.
* For this reason, it's recommended to consider it as consumed in any case and to not use it anymore.
* Consider using {@link SmartIterator.find} instead.
*
* If the iterator is infinite and every element satisfies the condition, the method will never return.
*
* ---
*
* @example
* ```ts
* const iterator = new SmartIterator<number>([-2, -1, 0, 1, 2]);
* const result = iterator.every((value) => value < 0);
*
* console.log(result); // false
* ```
*
* ---
*
* @param predicate The condition to check for each element of the iterator.
*
* @returns `true` if all elements satisfy the condition, `false` otherwise.
*/
every(e) {
let t = 0;
for (; ; ) {
const n = this._iterator.next();
if (n.done)
return !0;
if (!e(n.value, t))
return !1;
t += 1;
}
}
/**
* Determines whether any element of the iterator satisfies a given condition.
* See also {@link SmartIterator.every}.
*
* This method will iterate over all elements of the iterator checking if they satisfy the condition.
* Once a single element satisfies the condition, the method will return `true` immediately.
*
* This may lead to an unknown final state of the iterator, which may be entirely or partially consumed.
* For this reason, it's recommended to consider it as consumed in any case and to not use it anymore.
* Consider using {@link SmartIterator.find} instead.
*
* If the iterator is infinite and no element satisfies the condition, the method will never return.
*
* ---
*
* @example
* ```ts
* const iterator = new SmartIterator<number>([-2, -1, 0, 1, 2]);
* const result = iterator.some((value) => value < 0);
*
* console.log(result); // true
* ```
*
* ---
*
* @param predicate The condition to check for each element of the iterator.
*
* @returns `true` if any element satisfies the condition, `false` otherwise.
*/
some(e) {
let t = 0;
for (; ; ) {
const n = this._iterator.next();
if (n.done)
return !1;
if (e(n.value, t))
return !0;
t += 1;
}
}
filter(e) {
const t = this._iterator;
return new l(function* () {
let n = 0;
for (; ; ) {
const s = t.next();
if (s.done)
return s.value;
e(s.value, n) && (yield s.value), n += 1;
}
});
}
/**
* Maps the elements of the iterator using a given transformation function.
*
* This method will iterate over all elements of the iterator applying the transformation function.
* The result of each transformation will be included in the new iterator.
*
* Since the iterator is lazy, the mapping process will
* be executed once the resulting iterator is materialized.
*
* A new iterator will be created, holding the reference to the original one.
* This means that the original iterator won't be consumed until the
* new one is and that consuming one of them will consume the other as well.
*
* ---
*
* @example
* ```ts
* const iterator = new SmartIterator<number>([-2, -1, 0, 1, 2]);
* const result = iterator.map((value) => Math.abs(value));
*
* console.log(result.toArray()); // [2, 1, 0, 1, 2]
* ```
*
* ---
*
* @template V The type of the elements after the transformation.
*
* @param iteratee The transformation function to apply to each element of the iterator.
*
* @returns A new {@link SmartIterator} containing the transformed elements.
*/
map(e) {
const t = this._iterator;
return new l(function* () {
let n = 0;
for (; ; ) {
const s = t.next();
if (s.done)
return s.value;
yield e(s.value, n), n += 1;
}
});
}
reduce(e, t) {
let n = 0, s = t;
if (s === void 0) {
const r = this._iterator.next();
if (r.done)
throw new f("Cannot reduce an empty iterator without an initial value.");
s = r.value, n += 1;
}
for (; ; ) {
const r = this._iterator.next();
if (r.done)
return s;
s = e(s, r.value, n), n += 1;
}
}
/**
* Flattens the elements of the iterator using a given transformation function.
*
* This method will iterate over all elements of the iterator applying the transformation function.
* The result of each transformation will be flattened into the new iterator.
*
* Since the iterator is lazy, the flattening process will
* be executed once the resulting iterator is materialized.
*
* A new iterator will be created, holding the reference to the original one.
* This means that the original iterator won't be consumed until the
* new one is and that consuming one of them will consume the other as well.
*
* ---
*
* @example
* ```ts
* const iterator = new SmartIterator<number[]>([[-2, -1], 0, 1, 2, [3, 4, 5]]);
* const result = iterator.flatMap((value) => value);
*
* console.log(result.toArray()); // [-2, -1, 0, 1, 2, 3, 4, 5]
* ```
*
* ---
*
* @template V The type of the elements after the transformation.
*
* @param iteratee The transformation function to apply to each element of the iterator.
*
* @returns A new {@link SmartIterator} containing the flattened elements.
*/
flatMap(e) {
const t = this._iterator;
return new l(function* () {
let n = 0;
for (; ; ) {
const s = t.next();
if (s.done)
return s.value;
const r = e(s.value, n);
if (r instanceof Array)
for (const o of r)
yield o;
else
yield r;
n += 1;
}
});
}
/**
* Drops a given number of elements at the beginning of the iterator.
* The remaining elements will be included in a new iterator.
* See also {@link SmartIterator.take}.
*
* Since the iterator is lazy, the dropping process will
* be executed once the resulting iterator is materialized.
*
* A new iterator will be created, holding the reference to the original one.
* This means that the original iterator won't be consumed until the
* new one is and that consuming one of them will consume the other as well.
*
* Only the dropped elements will be consumed in the process.
* The rest of the iterator will be consumed only once the new one is.
*
* ---
*
* @example
* ```ts
* const iterator = new SmartIterator<number>([-2, -1, 0, 1, 2]);
* const result = iterator.drop(3);
*
* console.log(result.toArray()); // [1, 2]
* ```
*
* ---
*
* @param count The number of elements to drop.
*
* @returns A new {@link SmartIterator} containing the remaining elements.
*/
drop(e) {
const t = this._iterator;
return new l(function* () {
let n = 0;
for (; n < e; ) {
if (t.next().done)
return;
n += 1;
}
for (; ; ) {
const s = t.next();
if (s.done)
return s.value;
yield s.value;
}
});
}
/**
* Takes a given number of elements at the beginning of the iterator.
* These elements will be included in a new iterator.
* See also {@link SmartIterator.drop}.
*
* Since the iterator is lazy, the taking process will
* be executed once the resulting iterator is materialized.
*
* A new iterator will be created, holding the reference to the original one.
* This means that the original iterator won't be consumed until the
* new one is and that consuming one of them will consume the other as well.
*
* Only the taken elements will be consumed from the original iterator.
* The rest of the original iterator will be available for further consumption.
*
* ---
*
* @example
* ```ts
* const iterator = new SmartIterator<number>([-2, -1, 0, 1, 2]);
* const result = iterator.take(3);
*
* console.log(result.toArray()); // [-2, -1, 0]
* console.log(iterator.toArray()); // [1, 2]
* ```
*
* ---
*
* @param limit The number of elements to take.
*
* @returns A new {@link SmartIterator} containing the taken elements.
*/
take(e) {
const t = this._iterator;
return new l(function* () {
let n = 0;
for (; n < e; ) {
const s = t.next();
if (s.done)
return s.value;
yield s.value, n += 1;
}
});
}
find(e) {
let t = 0;
for (; ; ) {
const n = this._iterator.next();
if (n.done)
return;
if (e(n.value, t))
return n.value;
t += 1;
}
}
/**
* Enumerates the elements of the iterator.
* Each element is be paired with its index in a new iterator.
*
* Since the iterator is lazy, the enumeration process will
* be executed once the resulting iterator is materialized.
*
* A new iterator will be created, holding the reference to the original one.
* This means that the original iterator won't be consumed until the
* new one is and that consuming one of them will consume the other as well.
*
* ---
*
* @example
* ```ts
* const iterator = new SmartIterator<string>(["A", "M", "N", "Z"]);
* const result = iterator.enumerate();
*
* console.log(result.toArray()); // [[0, "A"], [1, "M"], [2, "N"], [3, "Z"]]
* ```
*
* ---
*
* @returns A new {@link SmartIterator} containing the enumerated elements.
*/
enumerate() {
return this.map((e, t) => [t, e]);
}
/**
* Removes all duplicate elements from the iterator.
* The first occurrence of each element will be kept.
*
* Since the iterator is lazy, the deduplication process will
* be executed once the resulting iterator is materialized.
*
* A new iterator will be created, holding the reference to the original one.
* This means that the original iterator won't be consumed until the
* new one is and that consuming one of them will consume the other as well.
*
* ---
*
* @example
* ```ts
* const iterator = new SmartIterator<number>([1, 1, 2, 3, 2, 3, 4, 5, 5, 4]);
* const result = iterator.unique();
*
* console.log(result.toArray()); // [1, 2, 3, 4, 5]
* ```
*
* ---
*
* @returns A new {@link SmartIterator} containing only the unique elements.
*/
unique() {
const e = this._iterator;
return new l(function* () {
const t = /* @__PURE__ */ new Set();
for (; ; ) {
const n = e.next();
if (n.done)
return n.value;
t.has(n.value) || (t.add(n.value), yield n.value);
}
});
}
/**
* Counts the number of elements in the iterator.
* This method will consume the entire iterator in the process.
*
* If the iterator is infinite, the method will never return.
*
* ---
*
* @example
* ```ts
* const iterator = new SmartIterator<number>([1, 2, 3, 4, 5]);
* const result = iterator.count();
*
* console.log(result); // 5
* ```
*
* ---
*
* @returns The number of elements in the iterator.
*/
count() {
let e = 0;
for (; ; ) {
if (this._iterator.next().done)
return e;
e += 1;
}
}
/**
* Iterates over all elements of the iterator.
* The elements are passed to the function along with their index.
*
* This method will consume the entire iterator in the process.
* If the iterator is infinite, the method will never return.
*
* ---
*
* @example
* ```ts
* const iterator = new SmartIterator<number>(["A", "M", "N", "Z"]);
* iterator.forEach((value, index) =>
* {
* console.log(`${index}: ${value}`); // "0: A", "1: M", "2: N", "3: Z"
* }
* ```
*
* ---
*
* @param iteratee The function to apply to each element of the iterator.
*/
forEach(e) {
let t = 0;
for (; ; ) {
const n = this._iterator.next();
if (n.done)
return;
e(n.value, t), t += 1;
}
}
/**
* Advances the iterator to the next element and returns the result.
* If the iterator requires it, a value must be provided to be passed to the next element.
*
* Once the iterator is done, the method will return an object with the `done` property set to `true`.
*
* ---
*
* @example
* ```ts
* const iterator = new SmartIterator<number>([1, 2, 3, 4, 5]);
*
* let result = iterator.next();
* while (!result.done)
* {
* console.log(result.value); // 1, 2, 3, 4, 5
*
* result = iterator.next();
* }
*
* console.log(result); // { done: true, value: undefined }
* ```
*
* ---
*
* @param values The value to pass to the next element, if required.
*
* @returns The result of the iteration, containing the value of the operation.
*/
next(...e) {
return this._iterator.next(...e);
}
/**
* An utility method that may be used to close the iterator gracefully,
* free the resources and perform any cleanup operation.
* It may also be used to signal the end or to compute a specific final result of the iteration process.
*
* ---
*
* @example
* ```ts
* const iterator = new SmartIterator<number>({
* _index: 0,
* next: function()
* {
* return { done: false, value: this._index += 1 };
* },
* return: function() { console.log("Closing the iterator..."); }
* });
*
* for (const value of iterator)
* {
* if (value > 5) { break; } // "Closing the iterator..."
*
* console.log(value); // 1, 2, 3, 4, 5
* }
* ```
*
* ---
*
* @param value The final value of the iterator.
*
* @returns The result of the iterator.
*/
return(e) {
return this._iterator.return ? this._iterator.return(e) : { done: !0, value: e };
}
/**
* An utility method that may be used to close the iterator due to an error,
* free the resources and perform any cleanup operation.
* It may also be used to signal that an error occurred during the iteration process or to handle it.
*
* ---
*
* @example
* ```ts
* const iterator = new SmartIterator<number>({
* _index: 0,
* next: function()
* {
* return { done: this._index > 10, value: this._index += 1 };
* },
* throw: function(error)
* {
* console.warn(error.message);
*
* this._index = 0;
* }
* });
*
* for (const value of iterator) // 1, 2, 3, 4, 5, "The index is too high.", 1, 2, 3, 4, 5, ...
* {
* try
* {
* if (value > 5) { throw new Exception("The index is too high."); }
*
* console.log(value); // 1, 2, 3, 4, 5
* }
* catch (error) { iterator.throw(error); }
* }
* ```
*
* ---
*
* @param error The error to throw into the iterator.
*
* @returns The final result of the iterator.
*/
throw(e) {
if (this._iterator.throw)
return this._iterator.throw(e);
throw e;
}
/**
* An utility method that aggregates the elements of the iterator using a given key function.
* The elements will be grouped by the resulting keys in a new specialized iterator.
* See {@link AggregatedIterator}.
*
* Since the iterator is lazy, the grouping process will
* be executed once the resulting iterator is materialized.
*
* A new iterator will be created, holding the reference to the original one.
* This means that the original iterator won't be consumed until the
* the new one is and that consuming one of them will consume the other as well.
*
* ---
*
* @example
* ```ts
* const iterator = new SmartIterator<number>([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
* const result = iterator.groupBy<string>((value) => value % 2 === 0 ? "even" : "odd");
*
* console.log(result.toObject()); // { odd: [1, 3, 5, 7, 9], even: [2, 4, 6, 8, 10] }
* ```
*
* ---
*
* @template K The type of the keys used to group the elements.
*
* @param iteratee The key function to apply to each element of the iterator.
*
* @returns A new instance of the {@link AggregatedIterator} class containing the grouped elements.
*/
groupBy(e) {
return new h(this.map((t, n) => [e(t, n), t]));
}
/**
* Materializes the iterator into an array.
* This method will consume the entire iterator in the process.
*
* If the iterator is infinite, the method will never return.
*
* ---
*
* @example
* ```ts
* const iterator = new SmartIterator(function* ()
* {
* for (let i = 0; i < 5; i += 1) { yield i; }
* });
* const result = iterator.toArray();
*
* console.log(result); // [0, 1, 2, 3, 4]
* ```
*
* ---
*
* @returns The {@link Array} containing all elements of the iterator.
*/
toArray() {
return Array.from(this);
}
[Symbol.toStringTag] = "SmartIterator";
[Symbol.iterator]() {
return this;
}
}
class u {
/**
* The internal {@link SmartIterator} object that holds the reduced elements.
*/
_elements;
constructor(e) {
this._elements = new l(e);
}
/**
* Determines whether all elements of the reduced iterator satisfy the given condition.
* See also {@link ReducedIterator.some}.
*
* This method will iterate over all the elements of the iterator checking if they satisfy the condition.
* Once a single element doesn't satisfy the condition, the method will return `false` immediately.
*
* This may lead to an unknown final state of the iterator, which may be entirely or partially consumed.
* For this reason, it's recommended to consider it as consumed in any case and to not use it anymore.
* Consider using {@link ReducedIterator.find} instead.
*
* If the iterator is infinite and every element satisfies the condition, the method will never return.
*
* ---
*
* @example
* ```ts
* const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
* .groupBy((value) => value % 2 === 0 ? "even" : "odd")
* .reduce((key, accumulator, value) => accumulator + value)
* .every((key, value) => value > 0);
*
* console.log(results); // true
* ```
*
* ---
*
* @param predicate The condition to check for each element of the iterator.
*
* @returns `true` if all elements satisfy the condition, `false` otherwise.
*/
every(e) {
for (const [t, [n, s]] of this._elements.enumerate())
if (!e(n, s, t))
return !1;
return !0;
}
/**
* Determines whether any element of the reduced iterator satisfies the given condition.
* See also {@link ReducedIterator.every}.
*
* This method will iterate over all the elements of the iterator checking if they satisfy the condition.
* Once a single element satisfies the condition, the method will return `true` immediately.
*
* This may lead to an unknown final state of the iterator, which may be entirely or partially consumed.
* For this reason, it's recommended to consider it as consumed in any case and to not use it anymore.
* Consider using {@link ReducedIterator.find} instead.
*
* If the iterator is infinite and no element satisfies the condition, the method will never return.
*
* ---
*
* @example
* ```ts
* const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
* .groupBy((value) => value % 2 === 0 ? "even" : "odd")
* .reduce((key, accumulator, value) => accumulator + value)
* .some((key, value) => value > 0);
*
* console.log(results); // true
* ```
*
* ---
*
* @param predicate The condition to check for each element of the iterator.
*
* @returns `true` if any element satisfies the condition, `false` otherwise.
*/
some(e) {
for (const [t, [n, s]] of this._elements.enumerate())
if (e(n, s, t))
return !0;
return !1;
}
filter(e) {
const t = this._elements.enumerate();
return new u(function* () {
for (const [n, [s, r]] of t)
e(s, r, n) && (yield [s, r]);
});
}
/**
* Maps the elements of the reduced iterator using a given transformation function.
*
* This method will iterate over all the elements of the iterator applying the transformation function.
* The result of the transformation will be included in the new iterator.
*
* Since the iterator is lazy, the mapping process will
* be executed once the resulting iterator is materialized.
*
* A new iterator will be created, holding the reference to the original one.
* This means that the original iterator won't be consumed until the
* new one is and that consuming one of them will consume the other as well.
*
* ---
*
* @example
* ```ts
* const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
* .groupBy((value) => value % 2 === 0 ? "even" : "odd")
* .reduce((key, accumulator, value) => accumulator + value)
* .map((key, value) => value * 2);
*
* console.log(results.toObject()); // { odd: 8, even: 32 }
* ```
*
* ---
*
* @template V The type of the elements after the transformation.
*
* @param iteratee The transformation function to apply to each element of the iterator.
*
* @returns A new {@link ReducedIterator} containing the transformed elements.
*/
map(e) {
const t = this._elements.enumerate();
return new u(function* () {
for (const [n, [s, r]] of t)
yield [s, e(s, r, n)];
});
}
reduce(e, t) {
let n = 0, s = t;
if (s === void 0) {
const r = this._elements.next();
if (r.done)
throw new f("Cannot reduce an empty iterator without an initial value.");
s = r.value[1], n += 1;
}
for (const [r, o] of this._elements)
s = e(r, s, o, n), n += 1;
return s;
}
/**
* Flattens the elements of the reduced iterator using a given transformation function.
*
* This method will iterate over all the elements of the iterator applying the transformation function.
* The result of each transformation will be flattened into the new iterator.
*
* Since the iterator is lazy, the flattening process will
* be executed once the resulting iterator is materialized.
*
* A new iterator will be created, holding the reference to the original one.
* This means that the original iterator won't be consumed until the
* new one is and that consuming one of them will consume the other as well.
*
* ---
*
* @example
* ```ts
* const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
* .groupBy((value) => value % 2 === 0 ? "even" : "odd")
* .reduce((key, accumulator, value) => accumulator.concat([value]), () => [])
* .flatMap((key, value) => value);
*
* console.log(results.toObject()); // { odd: [-3, -1, 3, 5], even: [0, 2, 6, 8] }
* ```
*
* ---
*
* @template V The type of the elements after the transformation.
*
* @param iteratee The transformation function to apply to each element of the iterator.
*
* @returns A new {@link AggregatedIterator} containing the flattened elements.
*/
flatMap(e) {
const t = this._elements.enumerate();
return new h(function* () {
for (const [n, [s, r]] of t) {
const o = e(s, r, n);
if (o instanceof Array)
for (const a of o)
yield [s, a];
else
yield [s, o];
}
});
}
/**
* Drops a given number of elements at the beginning of the reduced iterator.
* The remaining elements will be included in the new iterator.
* See also {@link ReducedIterator.take}.
*
* Since the iterator is lazy, the dropping process will
* be executed once the resulting iterator is materialized.
*
* A new iterator will be created, holding the reference to the original one.
* This means that the original iterator won't be consumed until the
* new one is and that consuming one of them will consume the other as well.
*
* Only the dropped elements will be consumed in the process.
* The rest of the iterator will be consumed once the new iterator is.
*
* ---
*
* @example
* ```ts
* const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
* .groupBy((value) => value % 2 === 0 ? "even" : "odd")
* .reduce((key, accumulator, value) => accumulator.concat(value), () => [])
* .drop(1);
*
* console.log(results.toObject()); // { even: [0, 2, 6, 8] }
* ```
*
* ---
*
* @param count The number of elements to drop.
*
* @returns A new {@link ReducedIterator} containing the remaining elements.
*/
drop(e) {
const t = this._elements.enumerate();
return new u(function* () {
for (const [n, [s, r]] of t)
n >= e && (yield [s, r]);
});
}
/**
* Takes a given number of elements at the beginning of the reduced iterator.
* The elements will be included in the new iterator.
* See also {@link ReducedIterator.drop}.
*
* Since the iterator is lazy, the taking process will
* be executed once the resulting iterator is materialized.
*
* A new iterator will be created, holding the reference to the original one.
* This means that the original iterator won't be consumed until the
* new one is and that consuming one of them will consume the other as well.
*
* Only the taken elements will be consumed from the original reduced iterator.
* The rest of the original reduced iterator will be available for further consumption.
*
* ---
*
* @example
* ```ts
* const reduced = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
* .groupBy((value) => value % 2 === 0 ? "even" : "odd")
* .reduce((key, accumulator, value) => accumulator.concat(value), () => []);
*
* const results = iterator.take(1);
*
* console.log(results.toObject()); // { odd: [-3, -1, 3, 5] }
* console.log(reduced.toObject()); // { even: [0, 2, 6, 8] }
* ```
*
* ---
*
* @param limit The number of elements to take.
*
* @returns A new {@link ReducedIterator} containing the taken elements.
*/
take(e) {
const t = this._elements.enumerate();
return new u(function* () {
for (const [n, [s, r]] of t) {
if (n >= e)
break;
yield [s, r];
}
});
}
find(e) {
for (const [t, [n, s]] of this._elements.enumerate())
if (e(n, s, t))
return s;
}
/**
* Enumerates the elements of the reduced iterator.
* Each element is paired with its index in a new iterator.
*
* Since the iterator is lazy, the enumeration process will
* be executed once the resulting iterator is materialized.
*
* A new iterator will be created, holding the reference to the original one.
* This means that the original iterator won't be consumed until the
* new one is and that consuming one of them will consume the other as well.
*
* ---
*
* @example
* ```ts
* const results = new ReducedIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
* .groupBy((value) => value % 2 === 0 ? "even" : "odd")
* .reduce((key, accumulator, value) => accumulator + value)
* .enumerate();
*
* console.log(results.toObject()); // [[0, 4], [1, 16]]
* ```
*
* ---
*
* @returns A new {@link ReducedIterator} object containing the enumerated elements.
*/
enumerate() {
return this.map((e, t, n) => [n, t]);
}
/**
* Removes all duplicate elements from the reduced iterator.
* The first occurrence of each element will be kept.
*
* Since the iterator is lazy, the deduplication process will
* be executed once the resulting iterator is materialized.
*
* A new iterator will be created, holding the reference to the original one.
* This means that the original iterator won't be consumed until the
* new one is and that consuming one of them will consume the other as well.
*
* ---
*
* @example
* ```ts
* const results = new ReducedIterator<number>([-3, -1, 0, 2, 3, 6, -3, -1, 1, 5, 6, 8, 7, 2])
* .groupBy((value) => value % 2 === 0 ? "even" : "odd")
* .map((key, value) => Math.abs(value))
* .reduce((key, accumulator, value) => accumulator + value)
* .unique();
*
* console.log(results.toObject()); // { odd: 24 }
*
* @returns A new {@link ReducedIterator} containing only the unique elements.
*/
unique() {
const e = this._elements;
return new u(function* () {
const t = /* @__PURE__ */ new Set();
for (const [n, s] of e)
t.has(s) || (t.add(s), yield [n, s]);
});
}
/**
* Counts the number of elements in the reduced iterator.
* This method will consume the entire iterator in the process.
*
* If the iterator is infinite, the method will never return.
*
* ---
*
* @example
* ```ts
* const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
* .groupBy((value) => value % 2 === 0 ? "even" : "odd")
* .reduce((key, accumulator, value) => accumulator + value)
* .count();
*
* console.log(results); // 2
* ```
*
* ---
*
* @returns The number of elements in the iterator.
*/
count() {
let e = 0;
for (const t of this._elements)
e += 1;
return e;
}
/**
* Iterates over all elements of the reduced iterator.
* The elements are passed to the function along with their key and index.
*
* This method will consume the entire iterator in the process.
* If the iterator is infinite, the method will never return.
*
* ---
*
* @example
* ```ts
* const reduced = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
* .groupBy((value) => value % 2 === 0 ? "even" : "odd")
* .reduce((key, accumulator, value) => accumulator + value);
*
* reduced.forEach((key, value, index) =>
* {
* console.log(`#${index} - ${key}: ${value}`); // "#0 - odd: 4", "#1 - even: 16"
* });
* ```
*
* ---
*
* @param iteratee The function to apply to each element of the reduced iterator.
*/
forEach(e) {
for (const [t, [n, s]] of this._elements.enumerate())
e(n, s, t);
}
/**
* Reaggregates the elements of the reduced iterator.
* The elements are grouped by a new key computed by the given iteratee function.
*
* Since the iterator is lazy, the reorganizing process will
* be executed once the resulting iterator is materialized.
*
* A new iterator will be created, holding the reference to the original one.
* This means that the original iterator won't be consumed until the
* new one is and that consuming one of them will consume the other as well.
*
* ---
*
* @example
* ```ts
* const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, -6, -8])
* .groupBy((value) => value % 2 === 0 ? "even" : "odd")
* .reduce((key, accumulator, value) => accumulator + value)
* .reorganizeBy((key, value) => value > 0 ? "positive" : "negative");
*
* console.log(results.toObject()); // { positive: 4, negative: -12 }
* ```
*
* ---
*
* @template J The type of the new keys used to group the elements.
*
* @param iteratee The function to determine the new key of each element of the iterator.
*
* @returns A new {@link AggregatedIterator} containing the elements reorganized by the new keys.
*/
reorganizeBy(e) {
const t = this._elements.enumerate();
return new h(function* () {
for (const [n, [s, r]] of t)
yield [e(s, r, n), r];
});
}
/**
* An utility method that returns a new {@link SmartIterator}
* object containing all the keys of the iterator.
*
* Since the iterator is lazy, the keys will be extracted
* be executed once the resulting iterator is materialized.
*
* A new iterator will be created, holding the reference to the original one.
* This means that the original iterator won't be consumed until the
* new one is and that consuming one of them will consume the other as well.
*
* ---
*
* @example
* ```ts
* const keys = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
* .groupBy((value) => value % 2 === 0 ? "even" : "odd")
* .reduce((key, accumulator, value) => accumulator + value)
* .keys();
*
* console.log(keys.toArray()); // ["odd", "even"]
* ```
*
* ---
*
* @returns A new {@link SmartIterator} containing all the keys of the iterator.
*/
keys() {
const e = this._elements;
return new l(function* () {
for (const [t] of e)
yield t;
});
}
/**
* An utility method that returns a new {@link SmartIterator}
* object containing all the entries of the iterator.
* Each entry is a tuple containing the key and the element.
*
* Since the iterator is lazy, the entries will be extracted
* be executed once the resulting iterator is materialized.
*
* A new iterator will be created, holding the reference to the original one.
* This means that the original iterator won't be consumed until the
* new one is and that consuming one of them will consume the other as well.
*
* ---
*
* @example
* ```ts
* const entries = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
* .groupBy((value) => value % 2 === 0 ? "even" : "odd")
* .reduce((key, accumulator, value) => accumulator + value)
* .entries();
*
* console.log(entries.toArray()); // [["odd", 4], ["even", 16]]
* ```
*
* ---
*
* @returns A new {@link SmartIterator} containing all the entries of the iterator.
*/
entries() {
return this._elements;
}
/**
* An utility method that returns a new {@link SmartIterator}
* object containing all the values of the iterator.
*
* Since the iterator is lazy, the values will be extracted
* be executed once the resulting iterator is materialized.
*
* A new iterator will be created, holding the reference to the original one.
* This means that the original iterator won't be consumed until the
* new one is and that consuming one of them will consume the other as well.
*
* ---
*
* @example
* ```ts
* const values = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
* .groupBy((value) => value % 2 === 0 ? "even" : "odd")
* .reduce((key, accumulator, value) => accumulator + value)
* .values();
*
* console.log(values.toArray()); // [4, 16]
* ```
*
* ---
*
* @returns A new {@link SmartIterator} containing all the values of the iterator.
*/
values() {
const e = this._elements;
return new l(function* () {
for (const [t, n] of e)
yield n;
});
}
/**
* Materializes the iterator into an array.
* This method will consume the entire iterator in the process.
*
* If the iterator is infinite, the method will never return.
*
* ---
*
* @example
* ```ts
* const reduced = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
* .groupBy((value) => value % 2 === 0 ? "even" : "odd")
* .reduce((key, accumulator, value) => accumulator + value);
*
* console.log(reduced.toArray()); // [4, 16]
* ```
*
* ---
*
* @returns The {@link Array} containing all elements of the iterator.
*/
toArray() {
return Array.from(this.values());
}
/**
* Materializes the iterator into a map.
* This method will consume the entire iterator in the process.
*
* If the iterator is infinite, the method will never return.
*
* ---
*
* @example
* ```ts
* const reduced = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
* .groupBy((value) => value % 2 === 0 ? "even" : "odd")
* .reduce((key, accumulator, value) => accumulator + value);
*
* console.log(reduced.toMap()); // Map(2) { "odd" => 4, "even" => 16 }
* ```
*
* ---
*
* @returns The {@link Map} containing all elements of the iterator.
*/
toMap() {
return new Map(this.entries());
}
/**
* Materializes the iterator into an object.
* This method will consume the entire iterator in the process.
*
* If the iterator is infinite, the method will never return.
*
* ---
*
* @example
* ```ts
* const reduced = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
* .groupBy((value) => value % 2 === 0 ? "even" : "odd")
* .reduce((key, accumulator, value) => accumulator + value);
*
* console.log(reduced.toObject()); // { odd: 4, even: 16 }
* ```
*
* ---
*
* @returns The {@link Object} containing all elements of the iterator.
*/
toObject() {
return Object.fromEntries(this.entries());
}
[Symbol.toStringTag] = "ReducedIterator";
}
class w {
/**
* The internal {@link SmartAsyncIterator} object that holds the elements to aggregate.
*/
_elements;
constructor(e) {
this._elements = new d(e);
}
/**
* Determines whether all elements of each group of the iterator satisfy a given condition.
* See also {@link AggregatedAsyncIterator.some}.
* This method will consume the entire iterator in the process.
*
* It will iterate over all elements of the iterator checjing if they satisfy the condition.
* Once a single element of one group doesn't satisfy the condition,
* the result for the respective group will be `false`.
*
* Eventually, it will return a new {@link ReducedIterator}
* object that will contain all the boolean results for each group.
* If the iterator is infinite, the method will never return.
*
* ---
*
* @example
* ```ts
* const results = new SmartAsyncIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
* .groupBy(async (value) => value % 2 === 0 ? "even" : "odd")
* .every(async (key, value) => value >= 0);
*
* console.log(await results.toObject()); // { odd: false, even: true }
* ```
*
* ---
*
* @param predicate The condition to check for each element of the iterator.
*
* @returns
* A {@link Promise} resolving to a new {@link ReducedIterator} containing the boolean results for each group.
*/
async every(e) {
const t = /* @__PURE__ */ new Map();
for await (const [n, s] of this._elements) {
const [r, o] = t.get(n) ?? [0, !0];
o