最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Reduce array of arrays of booleans to single array of booleans (Array<Array<boolean>> t

programmeradmin1浏览0评论

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 use map and reduce? 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
Add a ment  | 

3 Answers 3

Reset to default 4

You 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]

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论