【问题标题】:Sum values in 2D array based on lookup value - Javascript根据查找值对二维数组中的值求和 - Javascript
【发布时间】:2021-12-24 04:27:31
【问题描述】:

我有两个数组:

const array = [ 
[1, 7, 'AAA'], 
[2, 5, 'BBB'], 
[3, 2, 'CCC'], 
[4, 4, 'DDD'], 
[4, 9, 'EEE'], 
[4, 2, 'FFF'], 
[5, 8, 'GGG'], 
[6, 2, 'HHH']];

const names = [
[1, 'Joe'],
[2, 'Dave'],
[3, 'Mike'],
[4, 'Sandra'],
[5, 'Sue'],
[6, 'Mary']];

基于第一列中的值,我想对数组[1]中的值求和并列出三个字符的字母。我想要得到的结果是:

const names = [
[1, 'Joe',7,'AAA'],
[2, 'Dave',5,'BBB'],
[3, 'Mike',2,'CCC'],
[4, 'Sandra',15,'DDD, EEE, FFF'],
[5, 'Sue',8,'GGG'],
[6, 'Mary',2,'HHH']]

我不确定最好的方法,我对 Javascript 还很陌生。我设法做的是当 array[0] 中的值不重复时得到正确的结果,但我不能得到一个 sum 或 list 来工作。

const counter     = (array,value) => array.filter((v) => (v === value)).length;
const arrayCol    = (array,value) => array.map(v => v[value]);
const sum         = (prevVal, curVal) => prevVal + curVal;

names.forEach ((p,e) => {
array.forEach ((v,x) => (counter(arrayCol(array,0),v[0])===1) ?
(v[0]===p[0]) && names[e].push(v[1],v[2]) : 
(v[0]===p[0]) && names[e].push(array.reduce(sum,0)) );
});

console.log(names);

我确定答案与mapfilter 有关,但不确定如何...任何指针表示赞赏。谢谢

编辑:以下所有三个答案(来自 Michael HaddadNina Scholztesting_22)都有效且很有趣。

【问题讨论】:

    标签: javascript arrays multidimensional-array


    【解决方案1】:

    您可以收集每个组的所有数据,然后按名称数组的顺序映射结果。

    const
        array = [[1, 7, 'AAA'], [2, 5, 'BBB'], [3, 2, 'CCC'], [4, 4, 'DDD'], [4, 9, 'EEE'], [4, 2, 'FFF'], [5, 8, 'GGG'], [6, 2, 'HHH']],
        names = [[1, 'Joe'], [2, 'Dave'], [3, 'Mike'], [4, 'Sandra'], [5, 'Sue'], [6, 'Mary']],
        groups = array.reduce((r, [id, value, code]) => {
            r[id] ??= [0, ''];
            r[id][0] += value;
            r[id][1] += (r[id][1] && ', ') + code;
            return r;
        }, {}),
        result = names.map(a => [...a, ...groups[a[0]]]);
    
    console.log(result);
    .as-console-wrapper { max-height: 100% !important; top: 0; }

    【讨论】:

    • 谢谢@Nina-Scholz,当我试图弄清楚这一点时,我实际上正在阅读您的其他answers 之一。我没有考虑到 reduce 中的 currentValue 可能是一个数组,这真的很好。不过我遇到了一些麻烦,我使用的是 Google Apps Script,我认为它基本上是 Javascript,但是当我复制/粘贴时遇到的第一个错误是它不喜欢 = 登录 @ 987654327@,这是一个意外的令牌。如果我混淆了,我很抱歉。
    • 你可以用r[id] = r[id] || [0]替换r[id] ??= [0]
    • 谢谢你,这很有效,并且在阅读了logical OR 的使用后很有意义,非常好。如果可能的话,最后一种更改是保持数组的尺寸相同。我试过result = names.map(a => [...a, groups[a[0]].join(', ')]); 但我只想加入代码(当有多个代码时)并将值保留在它们的列中。我应该创建另一个groups,但只是为了值,然后再创建map?谢谢
    • 请参阅编辑。
    • 谢谢!看到你工作很有趣,为了我自己的理解改为= [0, '']r[id][1] += (r[id][1] && ', ') + code;(在阅读Logical AND 之后)是说如果有多个值然后返回', ' 如果不只是返回r[id][1]?并在添加代码后。最后,如何在每行末尾没有; 的情况下编写变量,这是为了提高性能吗?谢谢。
    【解决方案2】:

    您可以结合使用 map 和 reduce,如下所示:

    const array = [[1, 7, 'AAA'], [2, 5, 'BBB'], [3, 2, 'CCC'],[4, 4, 'DDD'], [4, 9, 'EEE'], [4, 2, 'FFF'], [5, 8, 'GGG'], [6, 2, 'HHH']];
    const names = [[1, 'Joe'],[2, 'Dave'],[3, 'Mike'],[4, 'Sandra'],[5, 'Sue'],[6, 'Mary']];
    
    const result = names.map(([id, name]) => {
        let vals = [];
        let sum = array.reduce((acc, [idx, number, XXX]) => 
          (idx === id ? (vals.push(XXX), number) : 0) + acc, 0);
        return [
          id, 
          name,
          sum, 
          vals.join(", ")
        ]
    })
    
    console.log(result)

    【讨论】:

    • 谢谢@testing_22,这几乎是正确的结果,只是当有多个id时,将代码连接在一起以使数组的维度保持不变。我一直在玩join(),但不能让它工作。我需要了解...
    • 哦,当然!我没有意识到。由于您想将代码数组连接在一起,因此您可以使用 join() 而不是 spread operator
    • 啊完美!我对 join() 的测试很接近,但不完全是,谢谢你的修复。谢谢你的链接,我需要阅读传播运算符。万事如意
    【解决方案3】:

    一个基本的方法可能是:

    const array = [[1, 7, 'AAA'], [2, 5, 'BBB'], [3, 2, 'CCC'], [4, 4, 'DDD'], [4, 9, 'EEE'], [4, 2, 'FFF'], [5, 8, 'GGG'], [6, 2, 'HHH']];
    const names = [[1, 'Joe'], [2, 'Dave'], [3, 'Mike'], [4, 'Sandra'], [5, 'Sue'], [6, 'Mary']];
    
    let result = [];
    for (let name of names) {
        let newValue = [...name, 0];
        let matchingItems = array.filter(i => i[0] === name[0]);
        let strings = []; // for lack of a better name...
    
        for (let item of matchingItems) {
            newValue[2] += item[1];
            strings.push(item[2]);
        }
        newValue.push(strings.join(", "));
    
        result.push(newValue);
    }
    
    console.log(result);

    您也可以自己实现连接逻辑(出于可读性原因,我实际上更喜欢这个版本):

       const array = [[1, 7, 'AAA'], [2, 5, 'BBB'], [3, 2, 'CCC'], [4, 4, 'DDD'], [4, 9, 'EEE'], [4, 2, 'FFF'], [5, 8, 'GGG'], [6, 2, 'HHH']];
    const names = [[1, 'Joe'], [2, 'Dave'], [3, 'Mike'], [4, 'Sandra'], [5, 'Sue'], [6, 'Mary']];
    
    let result = [];
    for (let name of names) {
        let newValue = [...name, 0, ""];
        let matchingItems = array.filter(i => i[0] === name[0]);
    
        for (let item of matchingItems) {
            newValue[2] += item[1];
            newValue[3] += newValue[3] === "" ? item[2] : `, ${item[2]}`;
        }
    
        result.push(newValue);
    }
    
    console.log(result);

    【讨论】:

    • 谢谢@Michael-Haddad 真的很有趣,我之前没有看到item of 使用过,但它是有道理的,并且将值与newValue[2] += item[1] 相加很清楚。我仍然遇到的唯一问题是当有多个代码 newValue = newValue.concat(item.slice(2)); 时,数组的维度从 4 列增加到 6 列
    • @TopMarx - 请考虑对答案的编辑。我相信它现在更好地回答了问题,并且更具可读性。
    • 再次感谢@Michael-Haddad 是的,这是有道理的:创建一个数组(“字符串”)以将值推送到循环内,然后再加入()。它完美地工作。只是为了帮助我理解扩展运算符,我将 [...name, 0] 更改为 name.slice().concat(0) 本质上扩展运算符只是使脚本更易于阅读,否则是否相同?谢谢
    • 不带参数调用slice 只会创建name 的副本,因此您不需要它。 [...name, 0]name.concat(0)name.slice().concat(0) 将具有相同的效果。对我来说,前者是更易读的版本,但由你来选择。
    • @TopMarx - 我添加了另一个版本,您不需要 strings 数组和 join 函数。
    猜你喜欢
    • 2020-12-10
    • 1970-01-01
    • 2017-04-24
    • 2012-05-17
    • 1970-01-01
    • 2023-03-02
    • 2013-09-13
    • 2019-03-31
    相关资源
    最近更新 更多