2

I have two arrays:

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']];

Based on the value in the first column, I want to sum the values in the array[1] and list the three-character letters. The result I'm trying to get is:

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

I'm not sure of the best approach, I'm fairly new to Javascript. What I've managed to do is get the right result when a value in array[0] isn't repeated, but I can't get a sum or list to work.

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

I'm sure the answer has to do with map or filter but not sure how... any pointers appreciated. Thank you

EDIT: All three answers below (from Michael Haddad, Nina Scholz, and testing_22) work and are interesting.

3 Answers 3

1

You can use a combination of map and reduce, as in:

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)

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you @testing_22, this is nearly the right result, only that when there is more than one id, to join the codes together so that the dimensions of the array remain the same. I've been playing around with join() but can't quite get it to work. And I need to learn about the ...
Oh, sure! I didn't realize it. Since you want to join the array of codes together then you can use join() instead of the spread operator
Ah perfect! I was close with my testing with join() but not quite, thank you for fixing. And thank you for the link, I need to read up on the spread operator. All the best
1

You could collect all data for each group and then map the result in order of the names array.

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

6 Comments

Thank you @Nina-Scholz I was actually reading one of your other answers earlier when I was trying to figure this out. I hadn't considered that the currentValue in reduce could be an array, that's really nice. I'm having some trouble with this though, I'm using Google Apps Script, which I think is basically Javascript, but the first error I get when I copy/paste is that it doesn't like the = sign in ??= [0], it's an unexpected token. I'm sorry if I'm confusing things.
you could replace r[id] ??= [0] with r[id] = r[id] || [0]
Thank you, that works, and makes sense after reading up on the use of logical OR, very nice. One final alteration, if possible, to keep the dimensions of the array the same. I've tried result = names.map(a => [...a, groups[a[0]].join(', ')]); but I just want to join the codes (when there's more than one) and keep the values in their column. Should I create another groups but just for the values and then map afterwards? thank you
please see edit.
Thank you! It's interesting to see you work, changing to = [0, ''] and r[id][1] += (r[id][1] && ', ') + code; for my own understanding (after reading Logical AND) is it saying if there's more than one value then return ', ' if not just return r[id][1]? and after add the code. And finally, how you write your variables without the ; at the end of each line, is that for a performance benefit? Thank you.
|
1

A basic approach could be:

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

You could also implement the joining logic yourself (I actually prefer this version for readability reasons):

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

7 Comments

Thank you @Michael-Haddad really interesting, I'd not seen item of used before but it makes sense, and summing the values with newValue[2] += item[1] is clear. The only trouble I'm still having is the dimensions of the array increasing from 4 columns to 6 when there is more than one code newValue = newValue.concat(item.slice(2)); I'm not trying to join 'DDD', 'EEE', 'FFF' together into single column
@TopMarx - Please consider the edits to the answer. I believe it answers the questions better now, and is also more readable.
Thank you again @Michael-Haddad yes that makes sense: create an array ('strings') to push values to within the loop and then join() after. It works perfectly. And just to help me understand the spread operator, I changed [...name, 0] to name.slice().concat(0) essentially the spread operator just makes the script easier to read otherwise it's the same? thank you
Calling slice with no arguments will just create a copy of name, so you do not need it. [...name, 0], name.concat(0) and name.slice().concat(0) will have the same effect. For me, the former is the more readable version but it's for you to make the choice.
@TopMarx - I have added another version, in which you don't need the strings array and the join function.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.