1 // ♓🌟 Piscēs ∷ collection.js
2 // ====================================================================
4 // Copyright © 2020–2022 Lady [@ Lady’s Computer].
6 // This Source Code Form is subject to the terms of the Mozilla Public
7 // License, v. 2.0. If a copy of the MPL was not distributed with this
8 // file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
10 import { call
, makeCallable
} from "./function.js";
16 MAXIMUM_SAFE_INTEGRAL_NUMBER
,
18 } from "./numeric.js";
19 import { sameValue
, type
} from "./value.js";
22 /** Returns an array of the provided values. */
25 /** Returns whether the provided value is an array. */
28 /** Returns an array created from the provided arraylike. */
33 * Returns -0 if the provided argument is "-0"; returns a number
34 * representing the index if the provided argument is a canonical
35 * numeric index string; otherwise, returns undefined.
37 * There is no clamping of the numeric index, but note that numbers
38 * above 2^53 − 1 are not safe nor valid integer indices.
40 export const canonicalNumericIndexString
= ($) => {
41 if (typeof $ !== "string") {
43 } else if ($ === "-0") {
47 return $ === `${n}` ? n
: undefined;
52 * Returns the result of catenating the provided arraylikes, returning
53 * a new collection according to the algorithm of Array::concat.
55 export const catenate
= makeCallable(Array
.prototype.concat
);
58 * Copies the items in the provided object to a new location according
59 * to the algorithm of Array::copyWithin.
61 export const copyWithin
= makeCallable(Array
.prototype.copyWithin
);
64 * Fills the provided object with the provided value using the
65 * algorithm of Array::fill.
67 export const fill
= makeCallable(Array
.prototype.fill
);
70 * Returns the result of filtering the provided object with the
71 * provided callback, using the algorithm of Array::filter.
73 export const filter
= makeCallable(Array
.prototype.filter
);
76 * Returns the first index in the provided object whose value satisfies
77 * the provided callback using the algorithm of Array::findIndex.
79 export const findIndex
= makeCallable(Array
.prototype.findIndex
);
82 * Returns the first indexed entry in the provided object whose value
83 * satisfies the provided callback.
85 * If a third argument is supplied, it will be used as the this value
88 export const findIndexedEntry
= (
93 let result
= undefined;
94 findItem($, (kValue
, k
, O
) => {
95 if (call(callback
, thisArg
, [kValue
, k
, O
])) {
96 // The callback succeeded.
100 // The callback failed.
108 * Returns the first indexed value in the provided object which
109 * satisfies the provided callback, using the algorithm of Array::find.
111 export const findItem
= makeCallable(Array
.prototype.find
);
114 * Returns the result of flatmapping the provided value with the
115 * provided callback using the algorithm of Array::flatMap.
117 export const flatmap
= makeCallable(Array
.prototype.flatMap
);
120 * Returns the result of flattening the provided object using the
121 * algorithm of Array::flat.
123 export const flatten
= makeCallable(Array
.prototype.flat
);
126 * Returns the first index of the provided object with a value
127 * equivalent to the provided value according to the algorithm of
130 export const getFirstIndex
= makeCallable(Array
.prototype.indexOf
);
133 * Returns the item on the provided object at the provided index using
134 * the algorithm of Array::at.
136 export const getItem
= makeCallable(Array
.prototype.at
);
139 * Returns the last index of the provided object with a value
140 * equivalent to the provided value according to the algorithm of
141 * Array::lastIndexOf.
143 export const getLastIndex
= makeCallable(Array
.prototype.lastIndexOf
);
146 * Returns whether every indexed value in the provided object satisfies
147 * the provided function, using the algorithm of Array::every.
149 export const hasEvery
= makeCallable(Array
.prototype.every
);
152 * Returns whether the provided object has an indexed value which
153 * satisfies the provided function, using the algorithm of Array::some.
155 export const hasSome
= makeCallable(Array
.prototype.some
);
158 * Returns whether the provided object has an indexed value equivalent
159 * to the provided value according to the algorithm of Array::includes.
161 * > ☡ This algorithm treats missing values as `undefined` rather than
164 export const includes
= makeCallable(Array
.prototype.includes
);
167 * Returns an iterator over the indexed entries in the provided value
168 * according to the algorithm of Array::entries.
170 export const indexedEntries
= makeCallable(Array
.prototype.entries
);
173 * Returns an iterator over the indices in the provided value according
174 * to the algorithm of Array::keys.
176 export const indices
= makeCallable(Array
.prototype.keys
);
178 /** Returns whether the provided value is an array index string. */
179 export const isArrayIndexString
= ($) => {
180 const value
= canonicalNumericIndexString($);
181 if (value
!== undefined) {
182 // The provided value is a canonical numeric index string.
183 return sameValue(value
, 0) || value
> 0 && value
< -1 >>> 0 &&
184 value
=== toLength(value
);
186 // The provided value is not a canonical numeric index string.
191 /** Returns whether the provided value is arraylike. */
192 export const isArraylikeObject
= ($) => {
193 if (type($) !== "object") {
197 lengthOfArraylike($); // throws if not arraylike
206 * Returns whether the provided object is a collection.
208 * The definition of “collection” used by Piscēs is similar to
209 * Ecmascript’s definition of an arraylike object, but it differs in
212 * - It requires the provided value to be a proper object.
214 * - It requires the `length` property to be an integer index.
216 * - It requires the object to be concat‐spreadable, meaning it must
217 * either be an array or have `[Symbol.isConcatSpreadable]` be true.
219 export const isCollection
= ($) => {
220 if (!(type($) === "object" && "length" in $)) {
221 // The provided value is not an object or does not have a `length`.
225 toIndex($.length
); // will throw if `length` is not an index
226 return isConcatSpreadable($);
234 * Returns whether the provided value is spreadable during array
237 * This is also used to determine which things should be treated as
240 export const isConcatSpreadable
= ($) => {
241 if (type($) !== "object") {
242 // The provided value is not an object.
245 // The provided value is an object.
246 const spreadable
= $[Symbol
.isConcatSpreadable
];
247 return spreadable
!== undefined ? !!spreadable
: isArray($);
251 /** Returns whether the provided value is an integer index string. */
252 export const isIntegerIndexString
= ($) => {
253 const value
= canonicalNumericIndexString($);
254 if (value
!== undefined && isIntegralNumber(value
)) {
255 // The provided value is a canonical numeric index string.
256 return sameValue(value
, 0) ||
257 value
> 0 && value
<= MAXIMUM_SAFE_INTEGRAL_NUMBER
&&
258 value
=== toLength(value
);
260 // The provided value is not a canonical numeric index string.
266 * Returns an iterator over the items in the provided value according
267 * to the algorithm of Array::values.
269 export const items
= makeCallable(Array
.prototype.values
);
272 * Returns the length of the provided arraylike object.
274 * Will throw if the provided object is not arraylike.
276 * This can produce larger lengths than can actually be stored in
277 * arrays, because no such restrictions exist on arraylike methods.
279 export const lengthOfArraylike
= ({ length
}) => toLength(length
);
282 * Returns the result of mapping the provided value with the provided
283 * callback using the algorithm of Array::map.
285 export const map
= makeCallable(Array
.prototype.map
);
287 /** Pops from the provided value using the algorithm of Array::pop. */
288 export const pop
= makeCallable(Array
.prototype.pop
);
291 * Pushes onto the provided value using the algorithm of Array::push.
293 export const push
= makeCallable(Array
.prototype.push
);
296 * Returns the result of reducing the provided value with the provided
297 * callback, using the algorithm of Array::reduce.
299 export const reduce
= makeCallable(Array
.prototype.reduce
);
302 * Reverses the provided value using the algorithm of Array::reverse.
304 export const reverse
= makeCallable(Array
.prototype.reverse
);
306 /** Shifts the provided value using the algorithm of Array::shift. */
307 export const shift
= makeCallable(Array
.prototype.shift
);
310 * Returns a slice of the provided value using the algorithm of
313 export const slice
= makeCallable(Array
.prototype.slice
);
316 * Sorts the provided value in‐place using the algorithm of
319 export const sort
= makeCallable(Array
.prototype.sort
);
322 * Splices into and out of the provided value using the algorithm of
325 export const splice
= makeCallable(Array
.prototype.splice
);
328 * Returns the result of converting the provided value to an array
329 * index, or throws an error if it is out of range.
331 export const toIndex
= ($) => {
332 const integer
= floor($);
333 if (isNan(integer
) || integer
== 0) {
334 // The value is zero·like.
337 // The value is not zero·like.
338 const clamped
= toLength(integer
);
339 if (clamped
!== integer
) {
340 // Clamping the value changes it.
341 throw new RangeError(`Piscēs: Index out of range: ${$}.`);
343 // The value is within appropriate bounds.
349 /** Returns the result of converting the provided value to a length. */
350 export const toLength
= ($) => {
351 const len
= floor($);
352 return isNan(len
) || len
== 0
354 : max(min(len
, MAXIMUM_SAFE_INTEGRAL_NUMBER
), 0);
358 * Unshifts the provided value using the algorithm of Array::unshift.
360 export const unshift
= makeCallable(Array
.prototype.unshift
);