1 // SPDX-FileCopyrightText: 2020, 2021, 2022, 2023, 2025 Lady <https://www.ladys.computer/about/#lady>
2 // SPDX-License-Identifier: MPL-2.0
4 * ⁌ ♓🧩 Piscēs ∷ collection.js
6 * Copyright © 2020–2023, 2025 Lady [@ Ladys Computer].
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
16 createCallableFunction
,
17 createProxyConstructor
,
20 } from "./function.js";
22 defineOwnDataProperty
,
26 isConcatSpreadableObject
,
31 toPropertyDescriptorRecord
,
34 canonicalNumericIndexString
,
36 MAXIMUM_SAFE_INTEGRAL_NUMBER
,
45 const PISC
ĒS
= "♓🧩 Piscēs";
47 /** Returns an array of the provided values. */
48 export const array
= createArrowFunction(
55 * Returns the result of catenating the provided concat spreadable
56 * values into a new collection according to the algorithm of
59 * ※ If no arguments are given, this function returns an empty
60 * array. This is different behaviour than if an explicit undefined
61 * first argument is given, in which case the resulting array will
62 * have undefined as its first item.
64 * ※ Unlike `Array::concat´, this function ignores
65 * `.constructor[Symbol.species]´ and always returns an array.
67 concatSpreadableCatenate
,
69 const { concat
} = Array
.prototype;
71 concatSpreadableCatenate: Object
.defineProperties(
72 (...args
) => call(concat
, [], args
),
74 name: { value: "concatSpreadableCatenate" },
82 * Copies the items in the provided object to a new location according
83 * to the algorithm of `Array::copyWithin´.
85 export const copyWithin
= createCallableFunction(
86 Array
.prototype.copyWithin
,
91 * Returns a proxy of the provided arraylike value for which every
92 * integer index less than its length will appear to be present.
94 * ※ The returned proxy will have the original object as its
95 * prototype and will update with changes to the original.
97 * ※ The returned proxy only reflects ⹐own⹑ properties on the
98 * underlying object; if an index is set on the prototype chain, but
99 * not as an own property on the underlying object, it will appear as
100 * undefined on the proxy. Like·wise, if the length is not an own
101 * property, it will appear to be zero.
103 * ※ Both data values and accessors are supported for indices,
104 * provided they are defined directly on the underlying object.
106 * ※ A proxy can be made non·extensible if the `.length´ of the
107 * underlying object is read·only (i·e, not defined using a getter)
108 * and nonconfigurable.
110 * ※ Integer indices on the returned proxy, as well as `.length´,
111 * are read·only and start out formally (but not manually)
112 * configurable. They can only be made nonconfigurable if the
113 * underlying value is guaranteed not to change. As a change in
114 * `.length´ deletes any integer indices larger than the `.length´,
115 * the `.length´ of the underlying object must be fixed for any
116 * integer index to be made nonconfigurable.
118 * ※ When iterating, it is probably faster to just make a copy of
119 * the original value, for example :—
121 * | `setPropertyValues(´
122 * | ` fill(setPropertyValue([], "length", original.length)),´
126 * This function is rather intended for the use·case where both the
127 * proxy and the underlying array are longlived, and the latter may
128 * change unexpectedly after the formers creation.
133 * Returns whether the provided value is a dense proxy (created with
139 deleteProperty: reflectDeleteProperty
,
140 defineProperty: reflectDefineProperty
,
141 getOwnPropertyDescriptor: reflectGetOwnPropertyDescriptor
,
142 getPrototypeOf: reflectGetPrototypeOf
,
144 isExtensible: reflectIsExtensible
,
145 ownKeys: reflectOwnKeys
,
146 preventExtensions: reflectPreventExtensions
,
147 setPrototypeOf: reflectSetPrototypeOf
,
149 const isReadOnlyNonconfigurable
= (Desc
) =>
151 && !(Desc
.configurable
|| "get" in Desc
152 || "writable" in Desc
&& Desc
.writable
);
154 const denseProxyHandler
= Object
.assign(Object
.create(null), {
155 defineProperty(O
, P
, Desc
) {
156 const k
= P
=== "length"
158 : canonicalNumericIndexString(P
);
162 && (sameValue(k
, 0) || k
> 0 && k
=== toLength(k
))
164 // The provided property is either `"length"´ or an integer
166 const desc
= maybe(Desc
, toPropertyDescriptorRecord
);
168 desc
.set !== UNDEFINED
169 || "writable" in desc
&& desc
.writable
171 // The provided descriptor defines a setter or is attempting
172 // to make the property writable; this is not permitted.
175 // The provided descriptor does not define a setter.
176 const Q
= reflectGetPrototypeOf(O
);
177 const current
= maybe(
178 reflectGetOwnPropertyDescriptor(O
, P
),
179 toPropertyDescriptorRecord
,
181 const lenDesc
= maybe(
182 reflectGetOwnPropertyDescriptor(Q
, "length"),
183 toPropertyDescriptorRecord
,
185 const currentlyConfigurable
= current
?.configurable
?? true;
186 const willChangeConfigurable
= "configurable" in desc
187 && (desc
.configurable
!== currentlyConfigurable
);
189 willChangeConfigurable
190 && (!currentlyConfigurable
191 || !isReadOnlyNonconfigurable(lenDesc
))
193 // The provided descriptor either aims to make a property
194 // nonconfigurable when the underlying length is not
195 // readonly or else aims to make a property configurable
196 // when it currently isn¦t; neither is permitted.
198 } else if (P
=== "length") {
199 // The provided descriptor is attempting to modify the
202 // It is known at this point that either the property
203 // descriptor is not trying to make the length
204 // nonconfigurable, or else the underlying length is
206 const len
= !currentlyConfigurable
208 : lenDesc
=== UNDEFINED
209 ? 0 // force zero if not an own property
211 ? null // not needed yet
212 : lengthOfArraylike(Q
);
215 || "enumerable" in desc
&& desc
.enumerable
216 || "value" in desc
&& desc
.value
!== len
218 // The provided descriptor is attempting to create a
219 // getter for the length, change the enumerability of the
220 // length, or change the value of the length; these are
223 } else if (!willChangeConfigurable
) {
224 // The provided descriptor is not attempting to change
225 // the value of the length or its configurablility; this
226 // succeeds with no effect.
229 // The provided descriptor is attempting to make a
230 // read·only, configurable length nonconfigurable.
231 return reflectDefineProperty(
234 setPropertyValues(objectCreate(null), {
237 value: len
?? lengthOfArraylike(Q
),
243 // The provided property is an integral canonical numeric
245 const len
= lenDesc
=== UNDEFINED
247 : lengthOfArraylike(Q
);
249 // The provided property is smaller than the length; this
250 // property can potentially be modified.
252 reflectGetOwnPropertyDescriptor(Q
, P
),
253 toPropertyDescriptorRecord
,
255 const kGet
= current
?.get; // use current getter
256 const kValue
= // use underlying value
259 : kDesc
.value
?? call(kDesc
.get, Q
, []);
261 "get" in desc
&& desc
.get !== kGet
262 || "enumerable" in desc
&& !desc
.enumerable
263 || "value" in desc
&& desc
.value
!== kValue
265 // The provided descriptor is attempting to change the
266 // value or enumerability of the property away from
267 // their current values; this is not permitted.
269 } else if (!willChangeConfigurable
) {
270 // The provided descriptor is not attempting to change
271 // the configurability of the property; in this case,
272 // no actual change is being requested.
275 // The provided descriptor is attempting to make the
276 // property nonconfigurable, but it is currently
277 // configurable (and maybe not even present on the
278 // proxy target); this is only permissible if the value
279 // of the underlying property is known not to be able
282 // Providing the value is okay if the underlying
283 // property is readonly, but if the underlying property
284 // is a getter, then the value must not be provided
285 // (since the resulting property will be defined with a
286 // brand new getter).
288 // At this point, it is known that the provided
289 // descriptor does not provide a getter, because
290 // getters are only supported on index properties which
291 // are already nonconfigurable.
293 // At this point, we have already confirmed that the
294 // length of the underlying object is immutable.
295 const dynamic
= kDesc
!== UNDEFINED
296 && !("writable" in kDesc
);
298 kDesc
=== UNDEFINED
&& !reflectIsExtensible(Q
)
299 || kDesc
!== UNDEFINED
&& !kDesc
.configurable
&& (
300 dynamic
|| !kDesc
.writable
302 const noChange
= !dynamic
303 || dynamic
&& !("value" in desc
);
304 return readonly
&& noChange
&& reflectDefineProperty(
309 kDesc
!== UNDEFINED
&& "get" in kDesc
313 get: defineOwnProperty(
314 () => call(kDesc
.get, Q
, []),
316 defineOwnDataProperty(
319 toFunctionName(P
, "get"),
334 // The provided property is not smaller than the length;
335 // this is not permitted.
341 // The provided property is not `"length"´ or an integer index.
342 return reflectDefineProperty(O
, P
, Desc
);
345 deleteProperty(O
, P
) {
346 const k
= P
=== "length"
348 : canonicalNumericIndexString(P
);
352 && (sameValue(k
, 0) || k
> 0 && k
=== toLength(k
))
354 // The property is an integer index or `"length"´.
355 if (!reflectIsExtensible(O
) || P
=== "length") {
356 // The proxied object is not extensible or the provided
357 // property is `"length"´; this is not permitted.
360 // The provided property is an integer index; it can only
361 // be deleted if it is greater than the length (in which
362 // case, it is not present in the first place).
363 const Q
= reflectGetPrototypeOf(O
);
364 const len
= hasOwnProperty(Q
, "length")
365 ? lengthOfArraylike(Q
)
367 return k
< len
? false : true;
370 // The provided property is not `"length"´ or an integer index.
371 return reflectDeleteProperty(O
, P
);
374 getOwnPropertyDescriptor(O
, P
) {
375 const k
= P
=== "length"
377 : canonicalNumericIndexString(P
);
381 && (sameValue(k
, 0) || k
> 0 && k
=== toLength(k
))
383 // The property is an integer index or `"length"´.
384 const Q
= reflectGetPrototypeOf(O
);
385 const current
= maybe(
386 reflectGetOwnPropertyDescriptor(O
, P
),
387 toPropertyDescriptorRecord
,
389 if (current
!== UNDEFINED
&& !current
.configurable
) {
390 // The property is defined and nonconfigurable on the object.
392 // Return its descriptor.
394 } else if (P
=== "length") {
395 // The property is `"length"´.
397 // Return the length of the underlying object.
398 return setPropertyValues(objectCreate(null), {
401 value: hasOwnProperty(Q
, "length")
402 ? lengthOfArraylike(Q
)
407 // The property is an integer index.
409 // Return a data descriptor with its value or undefined as
411 const len
= hasOwnProperty(Q
, "length")
412 ? lengthOfArraylike(Q
)
415 // The property is an integer index less than the length.
417 // Provide the current value of the own property.
419 reflectGetOwnPropertyDescriptor(Q
, P
),
420 toPropertyDescriptorRecord
,
422 return setPropertyValues(objectCreate(null), {
428 ? call(kDesc
.get, Q
, [])
433 // The property is an integer index, but not less than the
441 // The provided property is not `"length"´ or an integer index.
442 return reflectGetOwnPropertyDescriptor(O
, P
);
446 const k
= P
=== "length"
448 : canonicalNumericIndexString(P
);
449 if (P
=== "length") {
450 // The provided property is `"length"´; this is always present.
454 && (sameValue(k
, 0) || k
> 0 && k
=== toLength(k
))
456 // The provided property is an integer index.
458 // Return whether it is less than the length.
459 const Q
= reflectGetPrototypeOf(O
);
460 const len
= hasOwnProperty(Q
, "length")
461 ? lengthOfArraylike(Q
)
463 return k
< len
? true : reflectHas(O
, P
);
465 // The provided property is not `"length"´ or an integer index.
466 return reflectHas(O
, P
);
470 const keys
= reflectOwnKeys(O
);
471 const Q
= reflectGetPrototypeOf(O
);
472 const len
= hasOwnProperty(Q
, "length")
473 ? lengthOfArraylike(Q
)
477 let hasHitLength
= false;
478 for (i
= 0; i
< len
&& i
< -1 >>> 0; ++i
) {
479 // Iterate over those array indices which are less than the
480 // length of the underlying object and collect them in the
483 // Array indices are handled specially by the Ecmascript
484 // specification. Other integer indices may also be present
485 // (if they are too big to be array indices but still smaller
486 // than the length), but these are added later with all of the
488 defineOwnDataProperty(result
, i
, `${i}`);
490 for (let j
= 0; j
< keys
.length
; ++j
) {
491 // Iterate over the own keys of the object and collect them in
492 // the result if necessary.
494 const k
= P
=== "length"
496 : canonicalNumericIndexString(P
);
497 const isIntegerIndex
= k
!== UNDEFINED
498 && (sameValue(k
, 0) || k
> 0 && k
=== toLength(k
));
499 if (!hasHitLength
&& (!isIntegerIndex
|| k
>= -1 >>> 0)) {
500 // The current key is the first key which is not an array
501 // index; add `"length"´ to the result, as well as any
502 // integer indices which are not array indices.
504 // This may never occur, in which case these properties are
505 // added after the end of the loop.
507 // `"length"´ is added first as it is conceptually the first
508 // property on the object.
509 defineOwnDataProperty(result
, result
.length
, "length");
510 for (; i
< len
; ++i
) {
511 // Iterate over those remaining integer indices which are
512 // less than the length of the underlying object and
513 // collect them in the result.
514 defineOwnDataProperty(result
, result
.length
, `${i}`);
518 // The current key is not the first key which is not an array
522 if (P
=== "length" || isIntegerIndex
&& k
< len
) {
523 // The current key is either `"length"´ or an integer index
524 // less than the length; it has already been collected.
527 // The current key has not yet been collected into the
529 defineOwnDataProperty(result
, result
.length
, P
);
533 // All of the collected keys were array indices; `"length"´ and
534 // any outstanding integer indices still need to be collected.
535 defineOwnDataProperty(result
, result
.length
, "length");
536 for (; i
< len
; ++i
) {
537 // Iterate over those remaining integer indices which are
538 // less than the length of the underlying object and collect
539 // them in the result.
540 defineOwnDataProperty(result
, result
.length
, `${i}`);
543 // There was at least one key collected which was not an array
549 preventExtensions(O
) {
550 if (!reflectIsExtensible(O
)) {
551 // The object is already not extensible; this is an automatic
555 // The object is currently extensible; see if it can be made
556 // non·extensible and attempt to do so.
557 const Q
= reflectGetPrototypeOf(O
);
558 const lenDesc
= maybe(
559 reflectGetOwnPropertyDescriptor(Q
, "length"),
560 toPropertyDescriptorRecord
,
562 if (!isReadOnlyNonconfigurable(lenDesc
)) {
563 // The underlying length is not read·only; the object cannot
564 // be made non·extensible because the indices may change.
567 // The underlying length is read·only; define the needed
568 // indices on the object and then prevent extensions.
569 const len
= lengthOfArraylike(Q
); // definitely exists
570 for (let k
= 0; k
< len
; ++k
) {
571 // Iterate over each index and define a placeholder for it.
572 reflectDefineProperty(
575 setPropertyValues(objectCreate(null), {
583 return reflectPreventExtensions(O
);
587 setPrototypeOf(O
, V
) {
588 const Q
= reflectGetPrototypeOf(O
);
589 return Q
=== V
? reflectSetPrototypeOf(O
, V
) : false;
593 const DenseProxy
= createProxyConstructor(
596 return objectCreate(toObject($)); // throws if nullish
601 denseProxy: Object
.defineProperty(
602 ($) => new DenseProxy($),
604 { value: "denseProxy" },
606 isDenseProxy: DenseProxy
.isDenseProxy
,
611 * Fills the provided object with the provided value according to the
612 * algorithm of `Array::fill´.
614 export const fill
= createCallableFunction(Array
.prototype.fill
);
617 * Returns the result of filtering the provided object with the
618 * provided callback, according to the algorithm of `Array::filter´.
620 * ※ Unlike `Array::filter´, this function ignores
621 * `.constructor[Symbol.species]´ and always returns an array.
623 export const filter
= ($, callbackFn
, thisArg
= UNDEFINED
) => {
624 const O
= toObject($);
625 const len
= lengthOfArraylike(O
);
626 if (!isCallable(callbackFn
)) {
628 `${PISCĒS}: Filter callback must be callable.`,
632 for (let k
= 0, to
= 0; k
< len
; ++k
) {
635 if (call(callbackFn
, thisArg
, [kValue
, k
, O
])) {
636 defineOwnDataProperty(A
, to
++, kValue
);
649 * Returns the first index in the provided object whose value satisfies
650 * the provided callback.
652 * ※ This function differs from `Array::findIndex´ in that it returns
653 * undefined, not −1, if no match is found, and indices which aren¦t
654 * present are skipped, not treated as having values of undefined.
656 export const findFirstIndex
= ($, callback
, thisArg
= UNDEFINED
) =>
657 findFirstIndexedEntry($, callback
, thisArg
)?.[0];
661 * Returns the first indexed entry in the provided object whose value
662 * satisfies the provided callback.
664 * If a third argument is supplied, it will be used as the this value
667 * ※ Unlike the builtin Ecmascript array searching methods, this
668 * function does not treat indices which are not present on a sparse
669 * array as though they were undefined.
671 findFirstIndexedEntry
,
674 * Returns the last indexed entry in the provided object whose value
675 * satisfies the provided callback.
677 * If a third argument is supplied, it will be used as the this value
680 * ※ Unlike the builtin Ecmascript array searching methods, this
681 * function does not treat indices which are not present on a sparse
682 * array as though they were undefined.
684 findLastIndexedEntry
,
686 const findViaPredicate
= ($, direction
, predicate
, thisArg
) => {
687 const O
= toObject($);
688 const len
= lengthOfArraylike(O
);
689 if (!isCallable(predicate
)) {
690 // The provided predicate is not callable; throw an error.
692 `${PISCĒS}: Find predicate must be callable.`,
695 // The provided predicate is callable; do the search.
696 const ascending
= direction
=== "ascending";
698 let k
= ascending
? 0 : len
- 1;
699 ascending
? k
< len : k
>= 0;
700 ascending
? ++k : --k
702 // Iterate over each possible index between 0 and the length of
703 // the provided arraylike.
705 // The current index is not present in the provided value.
708 // The current index is present in the provided value; test
709 // to see if it satisfies the predicate.
711 if (call(predicate
, thisArg
, [kValue
, k
, O
])) {
712 // The value at the current index satisfies the predicate;
716 // The value at the current index does not satisfy the
727 findFirstIndexedEntry: ($, predicate
, thisArg
= UNDEFINED
) =>
728 findViaPredicate($, "ascending", predicate
, thisArg
),
729 findLastIndexedEntry: ($, predicate
, thisArg
= UNDEFINED
) =>
730 findViaPredicate($, "descending", predicate
, thisArg
),
735 * Returns the first indexed value in the provided object which
736 * satisfies the provided callback.
738 * ※ Unlike `Array::find´, this function does not treat indices which
739 * are not present on a sparse array as though they were undefined.
741 export const findFirstItem
= ($, callback
, thisArg
= UNDEFINED
) =>
742 findFirstIndexedEntry($, callback
, thisArg
)?.[1];
745 * Returns the last index in the provided object whose value satisfies
746 * the provided callback.
748 * ※ This function differs from `Array::findLastIndex´ in that it
749 * returns undefined, not −1, if no match is found, and indices which
750 * aren¦t present are skipped, not treated as having values of
753 export const findLastIndex
= ($, callback
, thisArg
= UNDEFINED
) =>
754 findLastIndexedEntry($, callback
, thisArg
)?.[0];
757 * Returns the last indexed value in the provided object which
758 * satisfies the provided callback.
760 * ※ Unlike `Array::findLast´, this function does not treat indices
761 * which are not present on a sparse array as though they were
764 export const findLastItem
= ($, callback
, thisArg
= UNDEFINED
) =>
765 findLastIndexedEntry($, callback
, thisArg
)?.[1];
768 * Returns the result of flatmapping the provided value with the
769 * provided callback according to the algorithm of `Array::flatMap´.
771 * ※ Flattening always produces a dense array.
773 export const flatmap
= createCallableFunction(
774 Array
.prototype.flatMap
,
779 * Returns the result of flattening the provided object according to
780 * the algorithm of `Array::flat´.
782 * ※ Flattening always produces a dense array.
784 export const flatten
= createCallableFunction(
785 Array
.prototype.flat
,
790 * Returns the first index of the provided object with a value
791 * equivalent to the provided value according to the algorithm of
794 export const getFirstIndex
= createCallableFunction(
795 Array
.prototype.indexOf
,
796 { name: "getFirstIndex" },
800 * Returns the item on the provided object at the provided index
801 * according to the algorithm of `Array::at´.
803 export const getItem
= createCallableFunction(
809 * Returns the last index of the provided object with a value
810 * equivalent to the provided value according to the algorithm of
811 * `Array::lastIndexOf´.
813 export const getLastIndex
= createCallableFunction(
814 Array
.prototype.lastIndexOf
,
815 { name: "getLastIndex" },
819 * Returns whether every indexed value in the provided object satisfies
820 * the provided function, according to the algorithm of `Array::every´.
822 export const hasEvery
= createCallableFunction(
823 Array
.prototype.every
,
824 { name: "hasEvery" },
828 * Returns whether the provided object has an indexed value which
829 * satisfies the provided function, according to the algorithm of
832 export const hasSome
= createCallableFunction(
833 Array
.prototype.some
,
838 * Returns whether the provided object has an indexed value equivalent
839 * to the provided value according to the algorithm of
842 * ※ This algorithm treats missing values as undefined rather than
845 export const includes
= createCallableFunction(
846 Array
.prototype.includes
,
850 * Returns an iterator over the indexed entries in the provided value
851 * according to the algorithm of `Array::entries´.
853 export const indexedEntries
= createCallableFunction(
854 Array
.prototype.entries
,
855 { name: "indexedEntries" },
859 * Returns an iterator over the indices in the provided value according
860 * to the algorithm of `Array::keys´.
862 export const indices
= createCallableFunction(
863 Array
.prototype.keys
,
867 export const isArray
= createArrowFunction(Array
.isArray
);
870 * Returns whether the provided object is a collection.
872 * The definition of “collection” used by Piscēs is similar to
873 * Ecmascripts definition of an arraylike object, but it differs in
876 * - It requires the provided value to be a proper object.
878 * - It requires the `length´ property to be an integer index.
880 * - It requires the object to be concat‐spreadable, meaning it must
881 * either be an array or have `.[Symbol.isConcatSpreadable]´ be true.
883 export const isCollection
= ($) => {
884 if (!(type($) === "object" && "length" in $)) {
885 // The provided value is not an object or does not have a `length´.
889 toIndex($.length
); // will throw if `length´ is not an index
890 return isConcatSpreadableObject($);
898 * Returns an iterator over the items in the provided value according
899 * to the algorithm of `Array::values´.
901 export const items
= createCallableFunction(
902 Array
.prototype.values
,
907 * Returns the result of mapping the provided value with the provided
908 * callback according to the algorithm of `Array::map´.
910 export const map
= createCallableFunction(Array
.prototype.map
);
913 * Pops from the provided value according to the algorithm of
916 export const pop
= createCallableFunction(Array
.prototype.pop
);
919 * Pushes onto the provided value according to the algorithm of
922 export const push
= createCallableFunction(Array
.prototype.push
);
925 * Returns the result of reducing the provided value with the provided
926 * callback, according to the algorithm of `Array::reduce´.
928 export const reduce
= createCallableFunction(Array
.prototype.reduce
);
931 * Reverses the provided value according to the algorithm of
934 export const reverse
= createCallableFunction(Array
.prototype.reverse
);
937 * Shifts the provided value according to the algorithm of
940 export const shift
= createCallableFunction(Array
.prototype.shift
);
943 * Returns a slice of the provided value according to the algorithm of
946 export const slice
= createCallableFunction(Array
.prototype.slice
);
949 * Sorts the provided value in‐place according to the algorithm of
952 export const sort
= createCallableFunction(Array
.prototype.sort
);
955 * Splices into and out of the provided value according to the
956 * algorithm of `Array::splice´.
958 export const splice
= createCallableFunction(Array
.prototype.splice
);
962 * Returns a potentially‐sparse array created from the provided
963 * arraylike or iterable.
965 * ※ This function differs from `Array.from´ in that it does not
966 * support subclassing and, in the case of a provided arraylike
967 * value, does not set properties on the result which are not present
968 * in the provided value. This can result in sparse arrays.
970 * ※ An iterator result which lacks a `value´ property also results
971 * in the corresponding index being missing in the resulting array.
976 * Returns a dense array created from the provided arraylike or
979 * ※ This function differs from `Array.from´ in that it does not
980 * support subclassing.
982 * ※ If indices are not present in a provided arraylike value, they
983 * will be treated exactly as tho they were present and set to
988 const makeArray
= Array
;
990 const arrayFrom
= (items
, mapFn
, thisArg
, allowSparse
= false) => {
991 // This function re·implements the behaviour of `Array.from´, plus
992 // support for sparse arrays and minus the support for subclassing.
994 // It is actually only needed in the former case, as subclassing
995 // support can more easily be removed by wrapping it in an arrow
996 // function or binding it to `Array´ or undefined.
997 if (mapFn
!== UNDEFINED
&& !isCallable(mapFn
)) {
998 // A mapping function was provided but is not callable; this is
1000 throw new TypeError(`${PISCĒS}: Map function not callable.`);
1002 // Attempt to get an iterator method for the provided items;
1003 // further behaviour depends on whether this is successful.
1004 const iteratorMethod
= getMethod(items
, ITERATOR
);
1005 if (iteratorMethod
!== UNDEFINED
) {
1006 // An iterator method was found; attempt to create an array
1009 const iterator
= call(items
, iteratorMethod
, []);
1010 if (type(iterator
) !== "object") {
1011 // The iterator method did not produce an object; this is an
1013 throw new TypeError(
1014 `${PISCĒS}: Iterators must be objects, but got: ${iterator}.`,
1017 // The iterator method produced an iterator object; collect
1018 // its values into the array.
1019 const nextMethod
= iterator
.next
;
1020 for (let k
= 0; true; ++k
) {
1021 // Loop until the iterator is exhausted.
1022 if (k
>= MAXIMUM_SAFE_INTEGRAL_NUMBER
) {
1023 // The current index has exceeded the maximum index
1024 // allowable for arrays; close the iterator, then throw
1027 // Attempt to close the iterator.
1030 // Ignore any errors while closing the iterator.
1033 throw new TypeError(`${PISCĒS}: Index out of range.`);
1035 // The current index is a valid index; get the next value
1036 // from the iterator and assign it, if one exists.
1037 const result
= call(nextMethod
, iterator
, []);
1038 if (type(result
) !== "object") {
1039 // The next method did not produce an object; this is
1041 throw new TypeError(
1042 `${PISCĒS}: Iterator results must be objects, but got: ${result}.`,
1045 // The next method produced an object; process it.
1046 const { done
} = result
;
1048 // The iterator has exhausted itself; confirm the
1049 // length of the resulting array and return it.
1053 const present
= "value" in result
;
1054 // The iterator has not exhausted itself; add its
1055 // value to the array.
1056 if (allowSparse
&& !present
) {
1057 // The iterator has no value and creating sparse
1058 // arrays is allowed.
1061 // The iterator has a value or sparse arrays are
1063 const nextValue
= present
1067 // Try to assign the value in the result array,
1068 // mapping if necessary.
1069 defineOwnDataProperty(
1073 ? call(mapFn
, thisArg
, [nextValue
, k
])
1077 // There was an error when mapping or assigning
1078 // the value; close the iterator before
1079 // rethrowing the error.
1081 // Attempt to close the iterator.
1084 // Ignore any errors while closing the
1097 // No iterator method was found; treat the provided items as an
1098 // arraylike object.
1099 const arraylike
= toObject(items
);
1100 const len
= lengthOfArraylike(arraylike
);
1101 const A
= makeArray(len
);
1102 for (let k
= 0; k
< len
; ++k
) {
1103 // Iterate over the values in the arraylike object and assign
1104 // them to the result array as necessary.
1105 const present
= k
in arraylike
;
1106 if (allowSparse
&& !present
) {
1107 // The current index is not present in the arraylike object
1108 // and sparse arrays are allowed.
1111 // The current index is present in the arraylike object or
1112 // sparse arrays are not allowed; assign the value to the
1113 // appropriate index in the result array, mapping if
1115 const nextValue
= present
? arraylike
[k
] : UNDEFINED
;
1116 defineOwnDataProperty(
1120 ? call(mapFn
, thisArg
, [nextValue
, k
])
1132 toArray: (items
, mapFn
= UNDEFINED
, thisArg
= UNDEFINED
) =>
1133 arrayFrom(items
, mapFn
, thisArg
, true),
1134 toDenseArray: (items
, mapFn
= UNDEFINED
, thisArg
= UNDEFINED
) =>
1135 arrayFrom(items
, mapFn
, thisArg
, false),
1140 * Unshifts the provided value according to the algorithm of
1143 export const unshift
= createCallableFunction(Array
.prototype.unshift
);