4

I have 2 2d arrays

var arr1 = [
  [1, 'a'],
  [2, 'b']
]

var arr2 = [
  [3, 'a'],
  [5, 'c']
]

I would like to sum these 2 arrays to get this result

var output = [
  [4, 'a'],
  [2, 'b'],
  [5, 'c']
]

I tried writing 2 .map functions but along with the desired results this will return a lot of duplicates:

function sumArrays (arr1, arr2) {
  var output = [];
  arr2.map(function(i) {
    arr1.map(function(n) {
      if (i[1] === n[1]) {
        output.push([i[0] + n[0], i[1]])
      } else {
        output.push(i)
      }
    })
  })
  return output;
}

Is there an easier way to do this, or should I now be removing everything but the highest value for a specific string?

Thanks for the help.

4
  • do you like to get a new array with the combined result? Commented Oct 22, 2016 at 11:39
  • I need to return the sum of both arrays. I can either do this by modifying the existing ones, or returning a new one. Commented Oct 22, 2016 at 11:40
  • Would be easier with a map, as in {a : 4, b : 2, c : 5} Commented Oct 22, 2016 at 11:43
  • arr1.concat(arr2).reduce((a,b)=>{return b[1] in a?a[b[1]]+=b[0]:a[b[1]]=b[0],a;},{}); Commented Oct 22, 2016 at 11:46

4 Answers 4

3

Please use not Array#map, when you do not need a new array, which this method returns.

You could use a hash table for the inventory and check against and update arr2 with Array#forEach.

Proposal which uses arr1 for update

var arr1 = [[1, 'a'], [2, 'b']],
    arr2 = [[3, 'a'], [5, 'c']],
    inventory = Object.create(null);

arr1.forEach(function (a) {
    this[a[1]] = a;
}, inventory);

arr2.forEach(function (a) {
    if (!this[a[1]]) {
        this[a[1]] = [0, a[1]];
        arr1.push(this[a[1]]);
    }
    this[a[1]][0] += a[0];
}, inventory);

console.log(arr1);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Proposal with new array for result.

var arr1 = [[1, 'a'], [2, 'b']],
    arr2 = [[3, 'a'], [5, 'c']],
    inventory = Object.create(null),
    result = arr1.map(function (a) {
        return this[a[1]] = [a[0], a[1]];
    }, inventory);

arr2.forEach(function (a) {
    if (!this[a[1]]) {
        this[a[1]] = [0, a[1]];
        result.push(this[a[1]]);
    }
    this[a[1]][0] += a[0];
}, inventory);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

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

5 Comments

Nice trick with the console
Good explanation. But, the expected result is an array, so why don't use an array?
@PraneshRavi, the result is an array, where don't you see an array?
Typo! Why don't you use map()?
you mean Map? ES6?
0

This question is essentially the same as this one, where I suggested the same ES6 code, using hashes (Set) and some variant code which I will not repeat here:

function sumArrays(a, b) {
    return Array.from(
        b.reduce( (m, [v,k]) => m.set(k, (m.get(k) || 0) + v),
                   new Map(a.map ( ([v,k]) => [k,v] )) ), // swap pairs
        ([k,v]) => [v,k]) // swap back afterwards;
}

var arr1 = [
  [1, 'a'],
  [2, 'b']
]

var arr2 = [
  [3, 'a'],
  [5, 'c']
]

var result = sumArrays(arr1, arr2);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Comments

0

You could also do this with forEach() and findIndex().

var arr1 = [
  [1, 'a'],
  [2, 'b']
]

var arr2 = [
  [3, 'a'],
  [5, 'c']
]

function sumArrays(arr1, arr2) {
  var r = arr1.slice(0);
  arr2.forEach(function(e) {
    var i = arr1.findIndex(function(a) {
      return e[1] == a[1];
    })
    i != -1 ? r[i][0] += e[0] : r.push(e)
  })
  return r;
}

console.log(sumArrays(arr1, arr2))

Comments

0

Concat both arrays, such that you have one array with all the tuples.

Then reduce that array of tuples, such that any compatible tuples get accumulated.

arr1.concat(arr2).reduce(function r(accumulator, iterand) {

    // base case: no tuples in accumulator
    if (accumulator.length == 0) {
        // add current tuple to our empty accumulator.
        return [iterand];
    }

    // first tuple in accumulator is compatible with the currently-inspected tuple
    if (accumulator[0][1] == iterand[1]) {
        // increment the count in the compatible tuple that already exists in the accumulator
        return [[accumulator[0][0]+iterand[0], accumulator[0][1]]].concat(accumulator.slice(1));
    }

    // currently-inspected tuple is not compatible with first tuple in accumulator. leave first tuple in accumulator unchanged. run the inductive case upon the remaining tuples in the accumulator.
    return [accumulator[0]].concat(r(accumulator.slice(1), iterand));

}, [])

This is a functional solution which will not mutate any of your existing input data.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.