1 | /**
|
2 | * Implements ES5 [`Array#forEach()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) method.<br><br>
|
3 | * Executes the provided callback once for each element.<br>
|
4 | * Callbacks are run concurrently,
|
5 | * and are only invoked for properties of the array that have been initialized (including those initialized with *undefined*), for unassigned ones `callback` is not run.<br>
|
6 | * @param {Array} array - Array to iterate over.
|
7 | * @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
8 | * @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
9 | * @return {Promise} - Returns a Promise with undefined value.
|
10 | */
|
11 | exports.forEach = async (array, callback, thisArg) => {
|
12 | const promiseArray = [];
|
13 | for (let i = 0; i < array.length; i++) {
|
14 | if (i in array) {
|
15 | const p = Promise.resolve(array[i]).then((currentValue) => {
|
16 | return callback.call(thisArg || this, currentValue, i, array);
|
17 | });
|
18 | promiseArray.push(p);
|
19 | }
|
20 | }
|
21 | await Promise.all(promiseArray);
|
22 | };
|
23 |
|
24 | /**
|
25 | * Same functionality as [`forEach()`](global.html#forEach), but runs only one callback at a time.
|
26 | * @param {Array} array - Array to iterate over.
|
27 | * @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
28 | * @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
29 | * @return {Promise} - Returns a Promise with undefined value.
|
30 | */
|
31 | exports.forEachSeries = async (array, callback, thisArg) => {
|
32 | for (let i = 0; i < array.length; i++) {
|
33 | await callback.call(thisArg || this, await array[i], i, array);
|
34 | }
|
35 | };
|
36 |
|
37 | /**
|
38 | * Implements ES5 [`Array#map()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) method.<br><br>
|
39 | * Creates a new array with the results of calling the provided callback once for each element.<br>
|
40 | * Callbacks are run concurrently,
|
41 | * and are only invoked for properties of the array that have been initialized (including those initialized with *undefined*), for unassigned ones`callback` is not run.<br>
|
42 | * Resultant *Array* is always the same *length* as the original one.
|
43 | * @param {Array} array - Array to iterate over.
|
44 | * @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
45 | * @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
46 | * @return {Promise} - Returns a Promise with the resultant *Array* as value.
|
47 | */
|
48 | exports.map = async (array, callback, thisArg) => {
|
49 | const promiseArray = [];
|
50 | for (let i = 0; i < array.length; i++) {
|
51 | if (i in array) {
|
52 | promiseArray[i] = Promise.resolve(array[i]).then((currentValue) => {
|
53 | return callback.call(thisArg || this, currentValue, i, array);
|
54 | });
|
55 | }
|
56 | }
|
57 | return Promise.all(promiseArray);
|
58 | };
|
59 |
|
60 | /**
|
61 | * Same functionality as [`map()`](global.html#map), but runs only one callback at a time.
|
62 | * @param {Array} array - Array to iterate over.
|
63 | * @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
64 | * @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
65 | * @return {Promise} - Returns a Promise with the resultant *Array* as value.
|
66 | */
|
67 | exports.mapSeries = async (array, callback, thisArg) => {
|
68 | const result = [];
|
69 | for (let i = 0; i < array.length; i++) {
|
70 | if (i in array) {
|
71 | result[i] = await callback.call(thisArg || this, await array[i], i, array);
|
72 | }
|
73 | }
|
74 | return result;
|
75 | };
|
76 |
|
77 | /**
|
78 | * Implements ES5 [`Array#find()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find) method.<br><br>
|
79 | * Returns the value of the element that satisfies the provided `callback`. The value returned is the one found first.<br>
|
80 | * Callbacks are run concurrently, meaning that all the callbacks are going to run even if the returned value is found in one of the first elements of `array`,
|
81 | * depending on the async calls you are going to use, consider using instead [`findSeries()`](global.html#findSeries).<br>
|
82 | * @param {Array} array - Array to iterate over.
|
83 | * @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
84 | * @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
85 | * @return {Promise} - Returns a Promise with the element that passed the test as value, otherwise *undefined*.
|
86 | */
|
87 | exports.find = (array, callback, thisArg) => {
|
88 | return new Promise((resolve, reject) => {
|
89 | if (array.length === 0) {
|
90 | return resolve();
|
91 | }
|
92 | let counter = 1;
|
93 | for (let i = 0; i < array.length; i++) {
|
94 | const check = (found) => {
|
95 | if (found) {
|
96 | resolve(array[i]);
|
97 | } else if (counter === array.length) {
|
98 | resolve();
|
99 | }
|
100 | counter++;
|
101 | };
|
102 | Promise.resolve(array[i])
|
103 | .then((elem) => callback.call(thisArg || this, elem, i, array))
|
104 | .then(check)
|
105 | .catch(reject);
|
106 | }
|
107 | });
|
108 | };
|
109 |
|
110 | /**
|
111 | * Same functionality as [`find()`](global.html#find), but runs only one callback at a time.
|
112 | * @param {Array} array - Array to iterate over.
|
113 | * @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
114 | * @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
115 | * @return {Promise} - Returns a Promise with the element that passed the test as value, otherwise *undefined*.
|
116 | */
|
117 | exports.findSeries = async (array, callback, thisArg) => {
|
118 | for (let i = 0; i < array.length; i++) {
|
119 | if (await callback.call(thisArg || this, await array[i], i, array)) {
|
120 | return array[i];
|
121 | }
|
122 | }
|
123 | };
|
124 |
|
125 | /**
|
126 | * Implements ES5 [`Array#findIndex()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex) method.<br><br>
|
127 | * Returns the index of the element that satisfies the provided `callback`. The index returned is the one found first.<br>
|
128 | * Callbacks are run concurrently, meaning that all the callbacks are going to run even if the returned index is found in one of the first elements of `array`,
|
129 | * depending on the async calls you are going to use, consider using instead [`findSeries()`](global.html#findSeries).<br>
|
130 | * @param {Array} array - Array to iterate over.
|
131 | * @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
132 | * @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
133 | * @return {Promise} - Returns a Promise with the index that passed the test as value, otherwise *-1*.
|
134 | */
|
135 | exports.findIndex = (array, callback, thisArg) => {
|
136 | return new Promise((resolve, reject) => {
|
137 | if (array.length === 0) {
|
138 | return resolve(-1);
|
139 | }
|
140 | let counter = 1;
|
141 | for (let i = 0; i < array.length; i++) {
|
142 | const check = (found) => {
|
143 | if (found) {
|
144 | resolve(i);
|
145 | } else if (counter === array.length) {
|
146 | resolve(-1);
|
147 | }
|
148 | counter++;
|
149 | };
|
150 | Promise.resolve(array[i])
|
151 | .then((elem) => callback.call(thisArg || this, elem, i, array))
|
152 | .then(check)
|
153 | .catch(reject);
|
154 | }
|
155 | });
|
156 | };
|
157 |
|
158 | /**
|
159 | * Same functionality as [`findIndex()`](global.html#findIndex), but runs only one callback at a time.
|
160 | * @param {Array} array - Array to iterate over.
|
161 | * @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
162 | * @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
163 | * @return {Promise} - Returns a Promise with the index that passed the test, otherwise *-1*.
|
164 | */
|
165 | exports.findIndexSeries = async (array, callback, thisArg) => {
|
166 | for (let i = 0; i < array.length; i++) {
|
167 | if (await callback.call(thisArg || this, await array[i], i, array)) {
|
168 | return i;
|
169 | }
|
170 | }
|
171 | };
|
172 |
|
173 | /**
|
174 | * Implements ES5 [`Array#some()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some) method.<br><br>
|
175 | * Test if some element in `array` passes the test implemented in `callback`.<br>
|
176 | * Callbacks are run concurrently, meaning that all the callbacks are going to run even if some of the first elements pass the test,
|
177 | * depending on the async calls you are going to use, consider using instead [`someSeries()`](global.html#someSeries).<br>
|
178 | * @param {Array} array - Array to iterate over.
|
179 | * @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
180 | * @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
181 | * @return {Promise} - Returns a Promise with *true* as value if some element passed the test, otherwise *false*.
|
182 | */
|
183 | exports.some = (array, callback, thisArg) => {
|
184 | return new Promise((resolve, reject) => {
|
185 | if (array.length === 0) {
|
186 | return resolve(false);
|
187 | }
|
188 | let counter = 1;
|
189 | for (let i = 0; i < array.length; i++) {
|
190 | if (!(i in array)) {
|
191 | counter++;
|
192 | continue;
|
193 | }
|
194 | const check = (found) => {
|
195 | if (found) {
|
196 | resolve(true);
|
197 | } else if (counter === array.length) {
|
198 | resolve(false);
|
199 | }
|
200 | counter++;
|
201 | };
|
202 | Promise.resolve(array[i])
|
203 | .then((elem) => callback.call(thisArg || this, elem, i, array))
|
204 | .then(check)
|
205 | .catch(reject);
|
206 | }
|
207 | });
|
208 | };
|
209 |
|
210 | /**
|
211 | * Same functionality as [`some()`](global.html#some), but runs only one callback at a time.
|
212 | * @param {Array} array - Array to iterate over.
|
213 | * @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
214 | * @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
215 | * @return {Promise} - Returns a Promise with *true* as value if some element passed the test, otherwise *false*.
|
216 | */
|
217 | exports.someSeries = async (array, callback, thisArg) => {
|
218 | for (let i = 0; i < array.length; i++) {
|
219 | if (await callback.call(thisArg || this, await array[i], i, array)) {
|
220 | return true;
|
221 | }
|
222 | }
|
223 | return false;
|
224 | };
|
225 |
|
226 | /**
|
227 | * Implements ES5 [`Array#every()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every) method.<br><br>
|
228 | * Test if all elements in `array` pass the test implemented in `callback`.<br>
|
229 | * Callbacks are run concurrently, meaning that all the callbacks are going to run even if any of the first elements do not pass the test,
|
230 | * depending on the async calls you are going to use, consider using instead [`everySeries()`](global.html#everySeries).<br>
|
231 | * @param {Array} array - Array to iterate over.
|
232 | * @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
233 | * @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
234 | * @return {Promise} - Returns a Promise with *true* as value if all elements passed the test, otherwise *false*.
|
235 | */
|
236 | exports.every = (array, callback, thisArg) => {
|
237 | return new Promise((resolve, reject) => {
|
238 | if (array.length === 0) {
|
239 | return resolve(true);
|
240 | }
|
241 | let counter = 1;
|
242 | for (let i = 0; i < array.length; i++) {
|
243 | if (!(i in array)) {
|
244 | counter++;
|
245 | continue;
|
246 | }
|
247 | const check = (found) => {
|
248 | if (!found) {
|
249 | resolve(false);
|
250 | } else if (counter === array.length) {
|
251 | resolve(true);
|
252 | }
|
253 | counter++;
|
254 | };
|
255 | Promise.resolve(array[i])
|
256 | .then((elem) => callback.call(thisArg || this, elem, i, array))
|
257 | .then(check)
|
258 | .catch(reject);
|
259 | }
|
260 | });
|
261 | };
|
262 |
|
263 | /**
|
264 | * Same functionality as [`every()`](global.html#every), but runs only one callback at a time.<br><br>
|
265 | * @param {Array} array - Array to iterate over.
|
266 | * @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
267 | * @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
268 | * @return {Promise} - Returns a Promise with *true* as value if all elements passed the test, otherwise *false*.
|
269 | */
|
270 | exports.everySeries = async (array, callback, thisArg) => {
|
271 | for (let i = 0; i < array.length; i++) {
|
272 | if (!await callback.call(thisArg || this, await array[i], i, array)) {
|
273 | return false;
|
274 | }
|
275 | }
|
276 | return true;
|
277 | };
|
278 |
|
279 | /**
|
280 | * Implements ES5 [`Array#filter()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) method.<br><br>
|
281 | * Creates a new array with the elements that passed the test implemented in `callback`.<br>
|
282 | * Callbacks are run concurrently.<br>
|
283 | * @param {Array} array - Array to iterate over.
|
284 | * @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
285 | * @param {Object} [thisArg] - Value to use as *this* when executing the `callback`.
|
286 | * @return {Promise} - Returns a Promise with the resultant filtered *Array* as value.
|
287 | */
|
288 | exports.filter = (array, callback, thisArg) => {
|
289 | /* two loops are necessary in order to do the filtering concurrently
|
290 | * while keeping the order of the elements
|
291 | * (if you find a better way to do it please send a PR!)
|
292 | */
|
293 | return new Promise(async (resolve, reject) => {
|
294 | const promiseArray = [];
|
295 | for (let i = 0; i < array.length; i++) {
|
296 | if (i in array) {
|
297 | promiseArray[i] = Promise.resolve(array[i]).then((currentValue) => {
|
298 | return callback.call(thisArg || this, currentValue, i, array);
|
299 | }).catch(reject);
|
300 | }
|
301 | }
|
302 | const filteredArray = [];
|
303 | for (let i = 0; i < promiseArray.length; i++) {
|
304 | if (await promiseArray[i]) {
|
305 | filteredArray.push(await array[i]);
|
306 | }
|
307 | }
|
308 | resolve(filteredArray);
|
309 | });
|
310 | };
|
311 |
|
312 | /**
|
313 | * Same functionality as [`filter()`](global.html#filter), but runs only one callback at a time.
|
314 | * @param {Array} array - Array to iterate over.
|
315 | * @param {Function} callback - Function to apply each item in `array`. Accepts three arguments: `currentValue`, `index` and `array`.
|
316 | * @return {Promise} - Returns a Promise with the resultant filtered *Array* as value.
|
317 | */
|
318 | exports.filterSeries = async (array, callback, thisArg) => {
|
319 | const result = [];
|
320 | for (let i = 0; i < array.length; i++) {
|
321 | if (i in array) {
|
322 | if (await callback.call(thisArg || this, await array[i], i, array)) {
|
323 | result.push(await array[i]);
|
324 | }
|
325 | }
|
326 | }
|
327 | return result;
|
328 | };
|
329 |
|
330 | /**
|
331 | * Implements ES5 [`Array#reduce()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce) method.<br><br>
|
332 | * Applies a `callback` against an accumulator and each element in `array`.
|
333 | * @param {Array} array - Array to iterate over.
|
334 | * @param {Function} callback - Function to apply each item in `array`. Accepts four arguments: `accumulator`, `currentValue`, `currentIndex` and `array`.
|
335 | * @param {Object} [initialValue] - Used as first argument to the first call of `callback`.
|
336 | * @return {Promise} - Returns a Promise with the resultant value from the reduction.
|
337 | */
|
338 | exports.reduce = async (array, callback, initialValue) => {
|
339 | if (array.length === 0 && !initialValue) {
|
340 | throw TypeError('Reduce of empty array with no initial value');
|
341 | }
|
342 | let i;
|
343 | let previousValue;
|
344 | if (initialValue) {
|
345 | previousValue = initialValue;
|
346 | i = 0;
|
347 | } else {
|
348 | previousValue = array[0];
|
349 | i = 1;
|
350 | }
|
351 | for (i; i < array.length; i++) {
|
352 | if (i in array) {
|
353 | previousValue = await callback(await previousValue, await array[i], i, array);
|
354 | }
|
355 | }
|
356 | return previousValue;
|
357 | };
|