This library provides a way to reduce an array and stop at some point WITHOUT ANY MUTATIONS. It does that by not trying to stop the reduction inside of the callback, but by providing a compare function which returns a boolean
.
The native Array.prototype.reduce()
has no mechanism, f.ex. like break
to stop the reduction at a given point, but it always iterates over the whole array.
Some people try to overcome this problem by using a for
-loop together with break
, which usually makes a lot of mutations necessary.
Some people use Array.prototype.reduce()
, but implement a mechanism which uses Array.prototype.splice()
to manipulate a clone of the original array, which again contains a mutation.
Some people provide a rewrite which hands over a callback, which sets a variable in the outer scope, so the whole process stops, finally returning the result. This still mutates a variable in the outer scope.
A final - and rather funny - approach is to use throw
in a try...catch
-block and stop the reduction this way, which works without any mutations, but abuses a mechanism with many unwanted side effects. It has a smell of "don't do this at home"!
For the discussion see Stackoverflow: https://stackoverflow.com/questions/36144406/how-to-early-break-reduce-method
This library provides a complete recursive rewrite, which calls a compare function before every new recursion, checking whether to stop or not. The approach is completely mutations free and clean. No hacks or workarounds required.
Copy the file /dist/array-reduce-compare.iife.min.js
and add the following to your HTML:
<script src="array-reduce-compare.iife.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
var arr = ['a', 'b', 'c', 'd'];
function join(acc, curr) {
return acc + curr;
}
console.log(
reduceCompare(
arr,
join,
function(acc) { return acc.length < 1; },
''
)
); // logs 'a'
console.log(
reduceCompare(
arr,
join,
function(acc, curr) { return curr !== 'c'; },
''
)
); // logs 'ab'
console.log(
reduceCompare(
arr,
join,
function(acc, curr, i) { return i < 3; },
''
)
); // logs 'abc'
// same as the native Array.prototype.reduce(), so try to avoid doing this
console.log(
reduceCompare(
arr,
join,
function() { return true; },
''
)
); // logs 'abcd'
var obj = {
'a': 'b',
'b': 'c',
'c': 'd',
'd': 'e'
};
console.log(
reduceCompare(
Object.keys(obj),
function(acc, key) {
var tmp = {};
tmp[obj[key]] = key;
return Object.assign({}, acc, tmp);
},
function(_, key) { return key !== 'd'; },
{}
)
);
/*
logs:
{
'b': 'a',
'c': 'b',
'd': 'c'
}
*/
});
</script>
Alternatively you can use a CDN like UNPKG or jsDelivr:
<script src="https://unpkg.com/array-reduce-compare/dist/array-reduce-compare.iife.min.js"></script>
or
<script src="https://cdn.jsdelivr.net/npm/array-reduce-compare/dist/array-reduce-compare.iife.min.js"></script>
import reduceCompare from 'array-reduce-compare';
document.addEventListener('DOMContentLoaded', () => {
const arr: Array<string> = ['a', 'b', 'c', 'd'];
function join(acc: string, curr: string): string {
return acc + curr;
}
console.log(
reduceCompare<string, string>(
arr,
join,
(acc: string): boolean => acc.length < 1,
''
)
); // logs 'a'
console.log(
reduceCompare<string, string>(
arr,
join,
(acc: string, curr: string): boolean => curr !== 'c',
''
)
); // logs 'ab'
console.log(
reduceCompare<string, string>(
arr,
join,
(acc: string, curr: string, i: number): boolean => i < 3,
''
)
); // logs 'abc'
// same as the native Array.prototype.reduce(), so try to avoid doing this
console.log(
reduceCompare<string, string>(
arr,
join,
(): boolean => true,
''
)
); // logs 'abcd'
const obj = {
'a': 'b',
'b': 'c',
'c': 'd',
'd': 'e'
};
console.log(
reduceCompare<string, Record<string, string>>(
Object.keys(obj),
(acc: Record<string, string>, key: string) => ({
...acc
, [obj[key]]: key
}),
(_, key: string): boolean => key !== 'd',
{}
)
);
/*
logs:
{
'b': 'a',
'c': 'b',
'd': 'c'
}
*/
});
function reduceCompare<A = unknown, B = unknown>(
arr: Array<A>
, cb: (
acc: B
, curr: A
, i: number
, arr: Array<A>
) => B
, cmp: (
acc: B
, curr: A
, i: number
, arr: Array<A>
) => boolean
, init?: B
): B;
The function arguments arr
for the array, cb
for the callback and init
for the initial value are the same as the ones in Array.prototype.reduce()
. The function also throws the same errors in the same situations.
The only new argument is the third argument cmp
, which is the compare function. It accepts the same argument as the callback function cb
. If it is undefined
or does not have the type function
, an error will be thrown. cmp
MUST return a boolean
, which indicates, whether to continue the reduction or not.
This software is brought to you with ❤️ love ❤️ from Dortmund and offered and distributed under the ISC license. See LICENSE.txt
and Wikipedia for more information.