1 | /**
|
2 | * Utility module to work with Arrays.
|
3 | *
|
4 | * @module array
|
5 | */
|
6 |
|
7 | import * as set from './set.js'
|
8 |
|
9 | /**
|
10 | * Return the last element of an array. The element must exist
|
11 | *
|
12 | * @template L
|
13 | * @param {ArrayLike<L>} arr
|
14 | * @return {L}
|
15 | */
|
16 | export const last = arr => arr[arr.length - 1]
|
17 |
|
18 | /**
|
19 | * @template C
|
20 | * @return {Array<C>}
|
21 | */
|
22 | export const create = () => /** @type {Array<C>} */ ([])
|
23 |
|
24 | /**
|
25 | * @template D
|
26 | * @param {Array<D>} a
|
27 | * @return {Array<D>}
|
28 | */
|
29 | export const copy = a => /** @type {Array<D>} */ (a.slice())
|
30 |
|
31 | /**
|
32 | * Append elements from src to dest
|
33 | *
|
34 | * @template M
|
35 | * @param {Array<M>} dest
|
36 | * @param {Array<M>} src
|
37 | */
|
38 | export const appendTo = (dest, src) => {
|
39 | for (let i = 0; i < src.length; i++) {
|
40 | dest.push(src[i])
|
41 | }
|
42 | }
|
43 |
|
44 | /**
|
45 | * Transforms something array-like to an actual Array.
|
46 | *
|
47 | * @function
|
48 | * @template T
|
49 | * @param {ArrayLike<T>|Iterable<T>} arraylike
|
50 | * @return {T}
|
51 | */
|
52 | export const from = Array.from
|
53 |
|
54 | /**
|
55 | * True iff condition holds on every element in the Array.
|
56 | *
|
57 | * @function
|
58 | * @template ITEM
|
59 | * @template {ArrayLike<ITEM>} ARR
|
60 | *
|
61 | * @param {ARR} arr
|
62 | * @param {function(ITEM, number, ARR):boolean} f
|
63 | * @return {boolean}
|
64 | */
|
65 | export const every = (arr, f) => {
|
66 | for (let i = 0; i < arr.length; i++) {
|
67 | if (!f(arr[i], i, arr)) {
|
68 | return false
|
69 | }
|
70 | }
|
71 | return true
|
72 | }
|
73 |
|
74 | /**
|
75 | * True iff condition holds on some element in the Array.
|
76 | *
|
77 | * @function
|
78 | * @template S
|
79 | * @template {ArrayLike<S>} ARR
|
80 | * @param {ARR} arr
|
81 | * @param {function(S, number, ARR):boolean} f
|
82 | * @return {boolean}
|
83 | */
|
84 | export const some = (arr, f) => {
|
85 | for (let i = 0; i < arr.length; i++) {
|
86 | if (f(arr[i], i, arr)) {
|
87 | return true
|
88 | }
|
89 | }
|
90 | return false
|
91 | }
|
92 |
|
93 | /**
|
94 | * @template ELEM
|
95 | *
|
96 | * @param {ArrayLike<ELEM>} a
|
97 | * @param {ArrayLike<ELEM>} b
|
98 | * @return {boolean}
|
99 | */
|
100 | export const equalFlat = (a, b) => a.length === b.length && every(a, (item, index) => item === b[index])
|
101 |
|
102 | /**
|
103 | * @template ELEM
|
104 | * @param {Array<Array<ELEM>>} arr
|
105 | * @return {Array<ELEM>}
|
106 | */
|
107 | export const flatten = arr => fold(arr, /** @type {Array<ELEM>} */ ([]), (acc, val) => acc.concat(val))
|
108 |
|
109 | /**
|
110 | * @template T
|
111 | * @param {number} len
|
112 | * @param {function(number, Array<T>):T} f
|
113 | * @return {Array<T>}
|
114 | */
|
115 | export const unfold = (len, f) => {
|
116 | const array = new Array(len)
|
117 | for (let i = 0; i < len; i++) {
|
118 | array[i] = f(i, array)
|
119 | }
|
120 | return array
|
121 | }
|
122 |
|
123 | /**
|
124 | * @template T
|
125 | * @template RESULT
|
126 | * @param {Array<T>} arr
|
127 | * @param {RESULT} seed
|
128 | * @param {function(RESULT, T, number):RESULT} folder
|
129 | */
|
130 | export const fold = (arr, seed, folder) => arr.reduce(folder, seed)
|
131 |
|
132 | export const isArray = Array.isArray
|
133 |
|
134 | /**
|
135 | * @template T
|
136 | * @param {Array<T>} arr
|
137 | * @return {Array<T>}
|
138 | */
|
139 | export const unique = arr => from(set.from(arr))
|
140 |
|
141 | /**
|
142 | * @template T
|
143 | * @template M
|
144 | * @param {ArrayLike<T>} arr
|
145 | * @param {function(T):M} mapper
|
146 | * @return {Array<T>}
|
147 | */
|
148 | export const uniqueBy = (arr, mapper) => {
|
149 | /**
|
150 | * @type {Set<M>}
|
151 | */
|
152 | const happened = set.create()
|
153 | /**
|
154 | * @type {Array<T>}
|
155 | */
|
156 | const result = []
|
157 | for (let i = 0; i < arr.length; i++) {
|
158 | const el = arr[i]
|
159 | const mapped = mapper(el)
|
160 | if (!happened.has(mapped)) {
|
161 | happened.add(mapped)
|
162 | result.push(el)
|
163 | }
|
164 | }
|
165 | return result
|
166 | }
|
167 |
|
168 | /**
|
169 | * @template {ArrayLike<any>} ARR
|
170 | * @template {function(ARR extends ArrayLike<infer T> ? T : never, number, ARR):any} MAPPER
|
171 | * @param {ARR} arr
|
172 | * @param {MAPPER} mapper
|
173 | * @return {Array<MAPPER extends function(...any): infer M ? M : never>}
|
174 | */
|
175 | export const map = (arr, mapper) => {
|
176 | /**
|
177 | * @type {Array<any>}
|
178 | */
|
179 | const res = Array(arr.length)
|
180 | for (let i = 0; i < arr.length; i++) {
|
181 | res[i] = mapper(/** @type {any} */ (arr[i]), i, /** @type {any} */ (arr))
|
182 | }
|
183 | return /** @type {any} */ (res)
|
184 | }
|