UNPKG

15.8 kBJavaScriptView Raw
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 */
11exports.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 */
31exports.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 */
48exports.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 */
67exports.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 */
87exports.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 */
117exports.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 */
135exports.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 */
165exports.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 */
183exports.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 */
217exports.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 */
236exports.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 */
270exports.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 */
288exports.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 */
318exports.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 */
338exports.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};