I have an array containing a random number of arrays of the same length with booleans.
arrBefore = [arr1, arr2, ..., arrn];
I want to return a single array of booleans containing true if any of values for the index is true and false otherwise.
arrBefore = [[true, false, false], [false, false, false], [true, false, true], [true, false, true]];
arrAfter = reduceMyArr(arrBefore);
console.log(arrAfter);
//[true, false, true]
I know how to do it with for loops. But I want to do it with map() and reduce() instead. I couldn't find a solution on stack. Any help would be greatly appreciated.
update 1
My example was poorly choosen since it led to some confusion in the awnsers. I want to pare the inner arrays indexes. So the result should be an array of the same lenght of the inner arrays. And arrAfter[i] should be true if arr1[i], arr2[i], ..., arrn[i] contains atleast 1 true and false otherwise.
update 2
Some more examples as requested in the ments.
arrBefore = [[true],[false],[true],[false]];
arrAfter = [true];
---
arrBefore = [[true, false],[false, false], [false, true], [false, false], [true, true]];
arrAfter = [true, true];
---
arrBefore = [[true, false, false, false], [true, true, false, false]];
arrAfter = [true, true, false, false];
---
arrBefore = [[true, true, false, false, false], [false, false, false, false, true]];
arrAfter = [true, true, false, false, true];
I have an array containing a random number of arrays of the same length with booleans.
arrBefore = [arr1, arr2, ..., arrn];
I want to return a single array of booleans containing true if any of values for the index is true and false otherwise.
arrBefore = [[true, false, false], [false, false, false], [true, false, true], [true, false, true]];
arrAfter = reduceMyArr(arrBefore);
console.log(arrAfter);
//[true, false, true]
I know how to do it with for loops. But I want to do it with map() and reduce() instead. I couldn't find a solution on stack. Any help would be greatly appreciated.
update 1
My example was poorly choosen since it led to some confusion in the awnsers. I want to pare the inner arrays indexes. So the result should be an array of the same lenght of the inner arrays. And arrAfter[i] should be true if arr1[i], arr2[i], ..., arrn[i] contains atleast 1 true and false otherwise.
update 2
Some more examples as requested in the ments.
arrBefore = [[true],[false],[true],[false]];
arrAfter = [true];
---
arrBefore = [[true, false],[false, false], [false, true], [false, false], [true, true]];
arrAfter = [true, true];
---
arrBefore = [[true, false, false, false], [true, true, false, false]];
arrAfter = [true, true, false, false];
---
arrBefore = [[true, true, false, false, false], [false, false, false, false, true]];
arrAfter = [true, true, false, false, true];
Share
Improve this question
edited Jan 14, 2020 at 7:16
VLAZ
29.1k9 gold badges63 silver badges84 bronze badges
asked Jan 13, 2020 at 9:36
Niels FergusonNiels Ferguson
3722 gold badges8 silver badges17 bronze badges
5
- Does this answer your question? Shortest way to "or" a list of booleans in javascript – VLAZ Commented Jan 13, 2020 at 9:39
-
Wait, you want to just OR the values of all equal indexes of the subarrays? Are you always going to have the same amount of items in each or can you have, say,
[ [true, true], [true] ]
? and if so, what is the expected output then? Oh, and do you absolutely need to usemap
andreduce
? Because there might be more appropriate tools. – VLAZ Commented Jan 13, 2020 at 12:06 - @VLAZ yes the returned array should always be the same length of the sub arrays. It is fine to use other tools then map() and reduce(). But want to avoid allot of nested loops. – Niels Ferguson Commented Jan 13, 2020 at 12:11
- Sorry, I meant what if the subarrays have a different length. Can that happen? Would it need to be handler and how? – VLAZ Commented Jan 13, 2020 at 12:14
- @VLAZ No the sub arrays have the same length by definition. – Niels Ferguson Commented Jan 13, 2020 at 12:18
3 Answers
Reset to default 4You can use a Generator function to make this task easier.
You can give it an array that contains sub-arrays and then let it walk each of the sub arrays grabbing the item at the same index from each and then OR-ing them. Here is an overview of how this would work:
start:
iteration 1:
mainArray = [ [a01, a02, ..., aNN], [b01, b02, ..., bNN], [c01, c02, ..., cNN] ]
^^^ ^^^ ^^^
pointer | | |
OR together: a01 || b01 || c01 --> result01
iteration 2:
mainArray = [ [a01, a02, ..., aNN], [b01, b02, ..., bNN], [c01, c02, ..., cNN] ]
^^^ ^^^ ^^^
pointer | | |
OR together: a02 || b02 || c02 --> result02
...
iteration NN:
mainArray = [ [a01, a02, ..., aNN], [b01, b02, ..., bNN], [c01, c02, ..., cNN] ]
^^^ ^^^ ^^^
pointer | | |
OR together: aNN || bNN || cNN --> resultNN
end
You can then use Array.from
to run through the entire algorithm and get an array from each iteration, so you'd get [result01, result02, ..., resultNN]
from the above.
Here is an implementation:
function* sequentialOR(mainArray) {
//convert from arrays to iterators over the arrays
const iterators = mainArray.map(subArray => subArray[Symbol.iterator]());
//call .next() on each iterator to grab the values
let allResults = iterators.map(it => it.next());
//continue until the first is done.
//Since all sub-arrays have the same length this will be the same for all
while(allResults[0].done === false) {
yield allResults
.map(({value}) => value) //get the boolean value
.some(Boolean); //(essentially) run an OR operation
allResults = iterators.map(it => it.next());
}
}
/*
arrBefore = [[true],[false],[true],[false]];
arrAfter = [true];
*/
const input1 = [[true],[false],[true],[false]];
test(input1);
/*
arrBefore = [[true, false],[false, false], [false, true], [false, false], [true, true]];
arrAfter = [true, true];
*/
const input2 = [[true, false],[false, false], [false, true], [false, false], [true, true]];
test(input2);
/*
arrBefore = [[true, false, false, false], [true, true, false, false]];
arrAfter = [true, true, false, false];
*/
const input3 = [[true, false, false, false], [true, true, false, false]];
test(input3);
/*
arrBefore = [[true, true, false, false, false], [false, false, false, false, true]];
arrAfter = [true, true, false, false, true];
*/
const input4 = [[true, true, false, false, false], [false, false, false, false, true]];
test(input4);
//a quick function to print the output
function test(input){
const output = Array.from(sequentialOR(input));
console.log(
`Input: ${JSON.stringify(input)}
Output: ${JSON.stringify(output)}`
);
}
I've chosen to use Array#some
here as it's a slightly more expressive way to implement OR. You can easily use Array#reduce
to achieve the same effect but since reduce
is more generic, it is slightly harder to understand that the operation is OR at a glance:
arrayOfBooleans.reduce((a, b) => a || b, false)
The initial value is set to false
as this is the neutral/identity element when it es to the OR operation. As you can see, while it might not be hard to see what's happening, it's not obvious at a glance, unlike .some
. However, it's also a valid way to derive the boolean result.
This approach can be further generalised to produce any result with each of the elements if you just supply it with a callback to run against each set of results. So, we can re-write it to be more generic like this:
function sequentialOperation(operation) {
return function* (mainArray) {
const iterators = mainArray.map(subArray => subArray[Symbol.iterator]());
let allResults = iterators.map(it => it.next());
while(allResults[0].done === false) {
yield operation(
allResults.map(({value}) => value)
)
allResults = iterators.map(it => it.next());
}
}
}
const sequentialOR = sequentialOperation(arr => arr.some(Boolean));
/*
arrBefore = [[true],[false],[true],[false]];
arrAfter = [true];
*/
const input1 = [[true],[false],[true],[false]];
test(input1);
/*
arrBefore = [[true, false],[false, false], [false, true], [false, false], [true, true]];
arrAfter = [true, true];
*/
const input2 = [[true, false],[false, false], [false, true], [false, false], [true, true]];
test(input2);
/*
arrBefore = [[true, false, false, false], [true, true, false, false]];
arrAfter = [true, true, false, false];
*/
const input3 = [[true, false, false, false], [true, true, false, false]];
test(input3);
/*
arrBefore = [[true, true, false, false, false], [false, false, false, false, true]];
arrAfter = [true, true, false, false, true];
*/
const input4 = [[true, true, false, false, false], [false, false, false, false, true]];
test(input4);
//a quick function to print the output
function test(input){
const output = Array.from(sequentialOR(input));
console.log(
`Input: ${JSON.stringify(input)}
Output: ${JSON.stringify(output)}`
);
}
Thus we can also easily derive other operations, for example:
const sequentialAND = sequentialOperation(arr => arr.every(Boolean)); //or arr.reduce((a, b) => a && b, true)
const sequentialAdd = sequentialOperation(arr => arr.reduce((a, b) => a + b, 0));
const sequentialMax = sequentialOperation(arr => arr.reduce((a, b) => Math.max(a, b), -Infinity));
//and so on
How to reduce an array of booleans to a single boolean? Well, it depends upon binary operation you'd like to use. In your case, obviously, or
is required, thus:
arrayOfBools.reduce((res, cur) => res || cur, false)
would work even if arrayOfBools
is empty (false
is default value returned in that way, which is logically correct).
How to reduce an array of arrays to a single array, i.e. how to flatten "higher order array" with reduce
?
higherOrderArray.reduce((res, cur) => res.concat(cur), [])
That will do.
Now you can flatten your array of arrays of bools to a single flat array of bools and reduce it to a single bool. Note that classic loop will be much more efficient.
The other way is to flat your higher order array differently, using map
. You could map your array of arrays of booleans to array of booleans using above mentioned or
-drived reduce
and reduce
it one more time.
Functional implementation would be:
const mapReduce = <TSource, TTarget, TReduced>
(map: (x: TSource) => TTarget) =>
(reduce: (res: TReduced, cur: TTarget) => TReduced) =>
(zero: TReduced) =>
(arr: TSource[]) : TReduced => {
return !arr || !arr.length ? zero : arr.map(map).reduce(reduce, zero)
}
P.S. I wouldn't suggest to let such code leak into your prod unless your team is absolutely fine with functional programming. It just servers for learning purposes.
let arrBefore = [true, false, false],[false, false, false],[true, false, true],[true, false, true]]
let arrAfter = []
arrBefore.forEach((item) => {
arrAfter.push(!!item.find((i) => i === true))
})
console.log(arrAfter)
// [true, false, true, true]