1 // ♓🌟 Piscēs ∷ collection.js 
   2 // ==================================================================== 
   4 // Copyright © 2020–2023 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"; 
  21 const { prototype: arrayPrototype 
} = Array
; 
  24   /** Returns an array of the provided values. */ 
  27   /** Returns whether the provided value is an array. */ 
  30   /** Returns an array created from the provided arraylike. */ 
  35  * Returns −0 if the provided argument is "-0"; returns a number 
  36  * representing the index if the provided argument is a canonical 
  37  * numeric index string; otherwise, returns undefined. 
  39  * There is no clamping of the numeric index, but note that numbers 
  40  * above 2^53 − 1 are not safe nor valid integer indices. 
  42 export const canonicalNumericIndexString 
= ($) => { 
  43   if (typeof $ !== "string") { 
  45   } else if ($ === "-0") { 
  49     return $ === `${n}` ? n : undefined; 
  54  * Returns the result of catenating the provided arraylikes into a new 
  55  * collection according to the algorithm of `Array::concat`. 
  57 export const catenate 
= makeCallable(arrayPrototype
.concat
); 
  60  * Copies the items in the provided object to a new location according 
  61  * to the algorithm of `Array::copyWithin`. 
  63 export const copyWithin 
= makeCallable(arrayPrototype
.copyWithin
); 
  66  * Fills the provided object with the provided value according to the 
  67  * algorithm of `Array::fill`. 
  69 export const fill 
= makeCallable(arrayPrototype
.fill
); 
  72  * Returns the result of filtering the provided object with the 
  73  * provided callback, according to the algorithm of `Array::filter`. 
  75 export const filter 
= makeCallable(arrayPrototype
.filter
); 
  78  * Returns the first index in the provided object whose value satisfies 
  79  * the provided callback according to the algorithm of 
  82 export const findIndex 
= makeCallable(arrayPrototype
.findIndex
); 
  85  * Returns the first indexed entry in the provided object whose value 
  86  * satisfies the provided callback. 
  88  * If a third argument is supplied, it will be used as the this value 
  91 export const findIndexedEntry 
= ( 
  96   let result 
= undefined; 
  97   findItem($, (kValue
, k
, O
) => { 
  98     if (call(callback
, thisArg
, [kValue
, k
, O
])) { 
  99       // The callback succeeded. 
 100       result 
= [k
, kValue
]; 
 103       // The callback failed. 
 111  * Returns the first indexed value in the provided object which 
 112  * satisfies the provided callback, according to the algorithm of 
 115 export const findItem 
= makeCallable(arrayPrototype
.find
); 
 118  * Returns the result of flatmapping the provided value with the 
 119  * provided callback according to the algorithm of `Array::flatMap`. 
 121 export const flatmap 
= makeCallable(arrayPrototype
.flatMap
); 
 124  * Returns the result of flattening the provided object according to 
 125  * the algorithm of `Array::flat`. 
 127 export const flatten 
= makeCallable(arrayPrototype
.flat
); 
 130  * Returns the first index of the provided object with a value 
 131  * equivalent to the provided value according to the algorithm of 
 134 export const getFirstIndex 
= makeCallable(arrayPrototype
.indexOf
); 
 137  * Returns the item on the provided object at the provided index 
 138  * according to the algorithm of `Array::at`. 
 140 export const getItem 
= makeCallable(arrayPrototype
.at
); 
 143  * Returns the last index of the provided object with a value 
 144  * equivalent to the provided value according to the algorithm of 
 145  * `Array::lastIndexOf`. 
 147 export const getLastIndex 
= makeCallable(arrayPrototype
.lastIndexOf
); 
 150  * Returns whether every indexed value in the provided object satisfies 
 151  * the provided function, according to the algorithm of `Array::every`. 
 153 export const hasEvery 
= makeCallable(arrayPrototype
.every
); 
 156  * Returns whether the provided object has an indexed value which 
 157  * satisfies the provided function, according to the algorithm of 
 160 export const hasSome 
= makeCallable(arrayPrototype
.some
); 
 163  * Returns whether the provided object has an indexed value equivalent 
 164  * to the provided value according to the algorithm of 
 167  * ※ This algorithm treats missing values as `undefined` rather than 
 170 export const includes 
= makeCallable(arrayPrototype
.includes
); 
 173  * Returns an iterator over the indexed entries in the provided value 
 174  * according to the algorithm of `Array::entries`. 
 176 export const indexedEntries 
= makeCallable(arrayPrototype
.entries
); 
 179  * Returns an iterator over the indices in the provided value according 
 180  * to the algorithm of `Array::keys`. 
 182 export const indices 
= makeCallable(arrayPrototype
.keys
); 
 184 /** Returns whether the provided value is an array index string. */ 
 185 export const isArrayIndexString 
= ($) => { 
 186   const value 
= canonicalNumericIndexString($); 
 187   if (value 
!== undefined) { 
 188     // The provided value is a canonical numeric index string. 
 189     return sameValue(value
, 0) || value 
> 0 && value 
< -1 >>> 0 && 
 190         value 
=== toLength(value
); 
 192     // The provided value is not a canonical numeric index string. 
 197 /** Returns whether the provided value is arraylike. */ 
 198 export const isArraylikeObject 
= ($) => { 
 199   if (type($) !== "object") { 
 203       lengthOfArraylike($); // throws if not arraylike 
 212  * Returns whether the provided object is a collection. 
 214  * The definition of “collection” used by Piscēs is similar to 
 215  * Ecmascript’s definition of an arraylike object, but it differs in 
 218  * - It requires the provided value to be a proper object. 
 220  * - It requires the `length` property to be an integer index. 
 222  * - It requires the object to be concat‐spreadable, meaning it must 
 223  *   either be an array or have `.[Symbol.isConcatSpreadable]` be true. 
 225 export const isCollection 
= ($) => { 
 226   if (!(type($) === "object" && "length" in $)) { 
 227     // The provided value is not an object or does not have a `length`. 
 231       toIndex($.length
); // will throw if `length` is not an index 
 232       return isConcatSpreadable($); 
 240  * Returns whether the provided value is spreadable during array 
 243  * This is also used to determine which things should be treated as 
 246 export const isConcatSpreadable 
= ($) => { 
 247   if (type($) !== "object") { 
 248     // The provided value is not an object. 
 251     // The provided value is an object. 
 252     const spreadable 
= $[Symbol
.isConcatSpreadable
]; 
 253     return spreadable 
!== undefined ? !!spreadable : isArray($); 
 257 /** Returns whether the provided value is an integer index string. */ 
 258 export const isIntegerIndexString 
= ($) => { 
 259   const value 
= canonicalNumericIndexString($); 
 260   if (value 
!== undefined && isIntegralNumber(value
)) { 
 261     // The provided value is a canonical numeric index string. 
 262     return sameValue(value
, 0) || 
 263       value 
> 0 && value 
<= MAXIMUM_SAFE_INTEGRAL_NUMBER 
&& 
 264         value 
=== toLength(value
); 
 266     // The provided value is not a canonical numeric index string. 
 272  * Returns an iterator over the items in the provided value according 
 273  * to the algorithm of `Array::values`. 
 275 export const items 
= makeCallable(arrayPrototype
.values
); 
 278  * Returns the length of the provided arraylike object. 
 280  * Will throw if the provided object is not arraylike. 
 282  * This can produce larger lengths than can actually be stored in 
 283  * arrays, because no such restrictions exist on arraylike methods. 
 285 export const lengthOfArraylike 
= ({ length 
}) => toLength(length
); 
 288  * Returns the result of mapping the provided value with the provided 
 289  * callback according to the algorithm of `Array::map`. 
 291 export const map 
= makeCallable(arrayPrototype
.map
); 
 294  * Pops from the provided value according to the algorithm of 
 297 export const pop 
= makeCallable(arrayPrototype
.pop
); 
 300  * Pushes onto the provided value according to the algorithm of 
 303 export const push 
= makeCallable(arrayPrototype
.push
); 
 306  * Returns the result of reducing the provided value with the provided 
 307  * callback, according to the algorithm of `Array::reduce`. 
 309 export const reduce 
= makeCallable(arrayPrototype
.reduce
); 
 312  * Reverses the provided value according to the algorithm of 
 315 export const reverse 
= makeCallable(arrayPrototype
.reverse
); 
 318  * Shifts the provided value according to the algorithm of 
 321 export const shift 
= makeCallable(arrayPrototype
.shift
); 
 324  * Returns a slice of the provided value according to the algorithm of 
 327 export const slice 
= makeCallable(arrayPrototype
.slice
); 
 330  * Sorts the provided value in‐place according to the algorithm of 
 333 export const sort 
= makeCallable(arrayPrototype
.sort
); 
 336  * Splices into and out of the provided value according to the 
 337  * algorithm of `Array::splice`. 
 339 export const splice 
= makeCallable(arrayPrototype
.splice
); 
 342  * Returns the result of converting the provided value to an array 
 343  * index, or throws an error if it is out of range. 
 345 export const toIndex 
= ($) => { 
 346   const integer 
= floor($); 
 347   if (isNan(integer
) || integer 
== 0) { 
 348     // The value is zero·like. 
 351     // The value is not zero·like. 
 352     const clamped 
= toLength(integer
); 
 353     if (clamped 
!== integer
) { 
 354       // Clamping the value changes it. 
 355       throw new RangeError(`Piscēs: Index out of range: ${$}.`); 
 357       // The value is within appropriate bounds. 
 363 /** Returns the result of converting the provided value to a length. */ 
 364 export const toLength 
= ($) => { 
 365   const len 
= floor($); 
 366   return isNan(len
) || len 
== 0 
 368     : max(min(len
, MAXIMUM_SAFE_INTEGRAL_NUMBER
), 0); 
 372  * Unshifts the provided value according to the algorithm of 
 375 export const unshift 
= makeCallable(arrayPrototype
.unshift
);