将布尔值数组的数组缩减为单个布尔值数组(Array<Array<boolean>> to Array<boolean>)

4

我有一个包含随机数量的布尔值数组,这些数组具有相同的长度。

arrBefore = [arr1, arr2, ..., arrn];

我想返回一个包含布尔值的单一数组,如果索引处的任何值为true,则该数组中对应的位置也为true,否则为false。
arrBefore = [[true, false, false], [false, false, false], [true, false, true], [true, false, true]];

arrAfter = reduceMyArr(arrBefore);

console.log(arrAfter);

//[true, false, true]

我知道如何用for循环实现,但我想改用map()和reduce()。我在站点上找不到解决方案。非常感谢任何帮助。
更新1:
我的示例选择不当,导致答案中有些混淆。我想比较内部数组的索引。因此,结果应该是一个与内部数组长度相同的数组。如果arr1[i]、arr2[i]、...、arrn[i]中至少包含一个true,则arrAfter[i]应为true,否则为false。
更新2:
根据评论中的要求,提供一些更多的示例。
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];

这个回答解决了你的问题吗?JavaScript中将布尔值列表“或”起来的最短方法 - VLAZ
等等,您想要对子数组中所有相同索引的值进行OR运算吗?每个子数组中的项数始终相同,还是可以有不同的项数,例如 [ [true, true], [true] ]?如果是这样,那么预期的输出是什么?哦,而且您是否绝对需要使用 mapreduce?因为可能有更合适的工具。 - VLAZ
@VLAZ 是的,返回的数组应该始终与子数组的长度相同。可以使用除map()和reduce()之外的其他工具。但是要避免使用大量嵌套循环。 - Niels Ferguson
抱歉,我的意思是如果子数组的长度不同会怎样。这种情况是否需要处理?如果需要,应该如何处理? - VLAZ
@VLAZ 不,根据定义,子数组具有相同的长度。 - Niels Ferguson
3个回答

4
你可以使用一个 生成器 函数使这个任务更容易。
你可以给它一个包含子数组的数组,然后让它遍历每个子数组,从每个子数组的相同索引处获取项目,然后进行OR运算。以下是这个过程的概述:
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

你可以使用 Array.from 来运行整个算法并从每次迭代中获取一个数组,因此您将从上述代码中获得 [result01, result02, ..., resultNN]
这里是一种实现方法:

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)}`
  );
}

我选择在这里使用Array#some,因为它是实现OR的一种更有表现力的方式。你可以轻松地使用Array#reduce来达到相同的效果,但由于reduce更通用,一眼看去稍微难以理解该操作是OR。
arrayOfBooleans.reduce((a, b) => a || b, false)

初始值设置为false,因为这是OR操作的中性/身份元素。正如你所见,虽然看起来并不是很难理解正在发生的事情,但一眼并不明显,不像.some。然而,这也是一种有效的推导布尔结果的方法。
如果您提供一个回调函数以针对每组结果运行,则可以进一步将此方法泛化以产生任何结果。因此,我们可以重写它以使其更加通用,如下所示:

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)}`
  );
}

因此,我们也可以轻松推导出其他操作,例如:
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

3
如何将布尔数组缩减为单个布尔值?这取决于您想使用的二进制操作。在您的情况下,显然需要使用“或”运算符,因此代码如下:or
arrayOfBools.reduce((res, cur) => res || cur, false)

即使 arrayOfBools 为空,也可以正常工作(在这种情况下返回默认值 false 是逻辑上正确的)。

如何使用 reduce 将数组的数组减少为单个数组,即如何展开“高阶数组”?

higherOrderArray.reduce((res, cur) => res.concat(cur), [])

可以了。

现在你可以将布尔值的数组转换为单一的平坦数组,并将其减少到单个布尔值。请注意,传统循环的效率要高得多。

另一种方法是使用map以不同的方式平铺您的高阶数组。您可以使用上述提到的基于orreduce将您的布尔值的数组映射到布尔值的数组,并将其再次reduce

函数实现如下:

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)

附言:我不建议将这样的代码泄露到您的生产环境中,除非您的团队完全接受函数式编程。它只是用于学习目的。


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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接