X-Git-Url: https://git.ladys.computer/Pisces/blobdiff_plain/8c8953d378b20e194535e22214750367961b6963..refs/heads/current:/collection.js?ds=inline diff --git a/collection.js b/collection.js index 6920661..d340709 100644 --- a/collection.js +++ b/collection.js @@ -1,67 +1,221 @@ // ♓🌟 Piscēs ∷ collection.js // ==================================================================== // -// Copyright © 2020–2022 Lady [@ Lady’s Computer]. +// Copyright © 2020–2023 Lady [@ Lady’s Computer]. // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at . -import { MAX_SAFE_INTEGER } from "./numeric.js"; -import { isObject } from "./object.js"; +import { call, createCallableFunction } from "./function.js"; +import { isConcatSpreadableObject } from "./object.js"; +import { toIndex, type } from "./value.js"; + +const { prototype: arrayPrototype } = Array; + +export const { + /** Returns an array of the provided values. */ + of: array, + + /** Returns whether the provided value is an array. */ + isArray, + + /** Returns an array created from the provided arraylike. */ + from: toArray, +} = Array; + +/** + * Returns the result of catenating the provided arraylikes into a new + * collection according to the algorithm of `Array::concat`. + */ +export const catenate = createCallableFunction( + arrayPrototype.concat, + "catenate", +); + +/** + * Copies the items in the provided object to a new location according + * to the algorithm of `Array::copyWithin`. + */ +export const copyWithin = createCallableFunction( + arrayPrototype.copyWithin, +); + +/** + * Fills the provided object with the provided value according to the + * algorithm of `Array::fill`. + */ +export const fill = createCallableFunction(arrayPrototype.fill); /** - * Returns -0 if the provided argument is "-0"; returns a number - * representing the index if the provided argument is a canonical - * numeric index string; otherwise, returns undefined. + * Returns the result of filtering the provided object with the + * provided callback, according to the algorithm of `Array::filter`. + */ +export const filter = createCallableFunction(arrayPrototype.filter); + +/** + * Returns the first index in the provided object whose value satisfies + * the provided callback according to the algorithm of + * `Array::findIndex`. + */ +export const findIndex = createCallableFunction( + arrayPrototype.findIndex, +); + +/** + * Returns the first indexed entry in the provided object whose value + * satisfies the provided callback. * - * There is no clamping of the numeric index, but note that numbers - * above 2^53 − 1 are not safe nor valid integer indices. - */ -export const canonicalNumericIndexString = ($) => { - if (typeof $ != "string") { - return undefined; - } else if ($ === "-0") { - return -0; - } else { - const n = +$; - return $ === `${n}` ? n : undefined; - } + * If a third argument is supplied, it will be used as the this value + * of the callback. + */ +export const findIndexedEntry = ( + $, + callback, + thisArg = undefined, +) => { + let result = undefined; + findItem($, (kValue, k, O) => { + if (call(callback, thisArg, [kValue, k, O])) { + // The callback succeeded. + result = [k, kValue]; + return true; + } else { + // The callback failed. + return false; + } + }); + return result; }; -/** Returns whether the provided value is an array index. */ -export const isArrayIndex = ($) => { - const value = canonicalNumericIndexString($); - if (value !== undefined) { - return Object.is(value, 0) || value > 0 && value < -1 >>> 0; - } else { - return false; - } -}; +/** + * Returns the first indexed value in the provided object which + * satisfies the provided callback, according to the algorithm of + * `Array::find`. + */ +export const findItem = createCallableFunction( + arrayPrototype.find, + "findItem", +); + +/** + * Returns the result of flatmapping the provided value with the + * provided callback according to the algorithm of `Array::flatMap`. + */ +export const flatmap = createCallableFunction( + arrayPrototype.flatMap, + "flatmap", +); + +/** + * Returns the result of flattening the provided object according to + * the algorithm of `Array::flat`. + */ +export const flatten = createCallableFunction( + arrayPrototype.flat, + "flatten", +); + +/** + * Returns the first index of the provided object with a value + * equivalent to the provided value according to the algorithm of + * `Array::indexOf`. + */ +export const getFirstIndex = createCallableFunction( + arrayPrototype.indexOf, + "getFirstIndex", +); + +/** + * Returns the item on the provided object at the provided index + * according to the algorithm of `Array::at`. + */ +export const getItem = createCallableFunction( + arrayPrototype.at, + "getItem", +); + +/** + * Returns the last index of the provided object with a value + * equivalent to the provided value according to the algorithm of + * `Array::lastIndexOf`. + */ +export const getLastIndex = createCallableFunction( + arrayPrototype.lastIndexOf, + "getLastIndex", +); + +/** + * Returns whether every indexed value in the provided object satisfies + * the provided function, according to the algorithm of `Array::every`. + */ +export const hasEvery = createCallableFunction( + arrayPrototype.every, + "hasEvery", +); + +/** + * Returns whether the provided object has an indexed value which + * satisfies the provided function, according to the algorithm of + * `Array::some`. + */ +export const hasSome = createCallableFunction( + arrayPrototype.some, + "hasSome", +); + +/** + * Returns whether the provided object has an indexed value equivalent + * to the provided value according to the algorithm of + * `Array::includes`. + * + * ※ This algorithm treats missing values as `undefined` rather than + * skipping them. + */ +export const includes = createCallableFunction( + arrayPrototype.includes, +); + +/** + * Returns an iterator over the indexed entries in the provided value + * according to the algorithm of `Array::entries`. + */ +export const indexedEntries = createCallableFunction( + arrayPrototype.entries, + "indexedEntries", +); + +/** + * Returns an iterator over the indices in the provided value according + * to the algorithm of `Array::keys`. + */ +export const indices = createCallableFunction( + arrayPrototype.keys, + "indices", +); /** * Returns whether the provided object is a collection. * * The definition of “collection” used by Piscēs is similar to * Ecmascript’s definition of an arraylike object, but it differs in - * a few ways :— + * a few ways :— * * - It requires the provided value to be a proper object. * - * - It requires the `length` property to be an integer corresponding - * to an integer index. + * - It requires the `length` property to be an integer index. * * - It requires the object to be concat‐spreadable, meaning it must - * either be an array or have `[Symbol.isConcatSpreadable]` be true. + * either be an array or have `.[Symbol.isConcatSpreadable]` be true. */ export const isCollection = ($) => { - if (!(isObject($) && "length" in $)) { + if (!(type($) === "object" && "length" in $)) { // The provided value is not an object or does not have a `length`. return false; } else { try { toIndex($.length); // will throw if `length` is not an index - return isConcatSpreadable($); + return isConcatSpreadableObject($); } catch { return false; } @@ -69,72 +223,70 @@ export const isCollection = ($) => { }; /** - * Returns whether the provided value is spreadable during array - * concatenation. - * - * This is also used to determine which things should be treated as - * collections. + * Returns an iterator over the items in the provided value according + * to the algorithm of `Array::values`. */ -export const isConcatSpreadable = ($) => { - if (!isObject($)) { - // The provided value is not an object. - return false; - } else { - // The provided value is an object. - return !!($[Symbol.isConcatSpreadable] ?? Array.isArray($)); - } -}; +export const items = createCallableFunction( + arrayPrototype.values, + "items", +); -/** Returns whether the provided value is an integer index. */ -export const isIntegerIndex = ($) => { - const value = canonicalNumericIndexString($); - if (value !== undefined) { - return Object.is(value, 0) || - value > 0 && value <= MAX_SAFE_INTEGER; - } else { - return false; - } -}; +/** + * Returns the result of mapping the provided value with the provided + * callback according to the algorithm of `Array::map`. + */ +export const map = createCallableFunction(arrayPrototype.map); /** - * Returns the length of the provided arraylike object. - * - * Will throw if the provided object is not arraylike. - * - * This produces larger lengths than can actually be stored in arrays, - * because no such restrictions exist on arraylike methods. Use - * `isIndex` to determine if a value is an actual array index. + * Pops from the provided value according to the algorithm of + * `Array::pop`. */ -export const lengthOfArrayLike = ({ length }) => { - return toLength(length); -}; +export const pop = createCallableFunction(arrayPrototype.pop); /** - * Converts the provided value to an array index, or throws an error if - * it is out of range. + * Pushes onto the provided value according to the algorithm of + * `Array::push`. */ -export const toIndex = ($) => { - const integer = Math.floor($); - if (isNaN(integer) || integer == 0) { - // The value is zero·like. - return 0; - } else { - // The value is not zero·like. - const clamped = toLength(integer); - if (clamped !== integer) { - // Clamping the value changes it. - throw new RangeError(`Piscēs: Index out of range: ${$}.`); - } else { - // The value is within appropriate bounds. - return integer; - } - } -}; +export const push = createCallableFunction(arrayPrototype.push); -/** Converts the provided value to a length. */ -export const toLength = ($) => { - const len = Math.floor($); - return isNaN(len) || len == 0 - ? 0 - : Math.max(Math.min(len, MAX_SAFE_INTEGER), 0); -}; +/** + * Returns the result of reducing the provided value with the provided + * callback, according to the algorithm of `Array::reduce`. + */ +export const reduce = createCallableFunction(arrayPrototype.reduce); + +/** + * Reverses the provided value according to the algorithm of + * `Array::reverse`. + */ +export const reverse = createCallableFunction(arrayPrototype.reverse); + +/** + * Shifts the provided value according to the algorithm of + * `Array::shift`. + */ +export const shift = createCallableFunction(arrayPrototype.shift); + +/** + * Returns a slice of the provided value according to the algorithm of + * `Array::slice`. + */ +export const slice = createCallableFunction(arrayPrototype.slice); + +/** + * Sorts the provided value in‐place according to the algorithm of + * `Array::sort`. + */ +export const sort = createCallableFunction(arrayPrototype.sort); + +/** + * Splices into and out of the provided value according to the + * algorithm of `Array::splice`. + */ +export const splice = createCallableFunction(arrayPrototype.splice); + +/** + * Unshifts the provided value according to the algorithm of + * `Array::unshift`. + */ +export const unshift = createCallableFunction(arrayPrototype.unshift);