Join array elements if another array have same elements

Viewed 137

I have two arrays

var arrayA = ["a", "a", "b", "b", "a", "c"];
var arrayB = [10, 20, 3, 2, 20, 5];

As you can see, arrayA[0], arrayA[1], arrayA[4] have an same elements (arrayA[2], arrayA[3] same too).

So based on above example, I want arrayB[0], arrayB[1], arrayB[4] will be summed up, and arrayB[2], arrayB[3] too.

expectation output

arrayA = ["a", "b", "c"];
arrayB = [50, 5, 5];

It's possible to sum the arrayB elements if arrayA have same elements based arrayA index? and is there an Lodash/Underscore function to do this?

8 Answers

You could use an object for the indices and maintain the values.

var arrayA = ["a", "a", "b", "b", "a", "c"],
    arrayB = [10, 20, 3, 2, 20, 5],
    indices = Object.create(null),
    groupedA = [],
    groupedB = [];
    
arrayA.forEach(function (a, i) {
    if (!(a in indices)) {
        groupedA.push(a);
        indices[a] = groupedB.push(0) - 1;
    }
    groupedB[indices[a]] += arrayB[i];
});

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

Version which mutates the original arrays.

var arrayA = ["a", "a", "b", "b", "a", "c"],
    arrayB = [10, 20, 3, 2, 20, 5],
    indices = Object.create(null),
    i = 0;

while (i < arrayA.length) {
    if (!(arrayA[i] in indices)) {
        indices[arrayA[i]] = i++;
        continue;
    }
    arrayB[indices[arrayA[i]]] += arrayB.splice(i, 1)[0];
    arrayA.splice(i, 1);
}

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

Here's a solution using lodash:

[arrayA, arrayB] = _(arrayA)
    .zip(arrayB)
    .groupBy(0)
    .mapValues( grp => _.sumBy(grp,1))
    .thru(obj => [_.keys(obj), _.values(obj)])
    .value();

zip will associate each element in arrayA with the corresponding element in arrayB e.g. [ ['a', 10], ['a', 20], ...]

We then groupBy the value in position 0 giving an object something like:

{
   a: ['a', 10], ['a', 20], ['a', 20'],
   b: ['b', 3] ...,
   c: ...
}

The values of each key are then mapped to the sum of the values in position 1 before finally returning the keys and the values in separate arrays.

You can reduce both arrays into an ES6 Map, and then spread the keys for arrayA, and the values for arrayB:

const arrayA = ["a", "a", "b", "b", "a", "c"];
const arrayB = [10, 20, 3, 2, 20, 5];

const map = arrayA.reduce((m, c, i) => m.set(c, (m.get(c) || 0) + arrayB[i]), new Map());

const arrA = [...map.keys()];
const arrB = [...map.values()];

console.log(arrA);

console.log(arrB);

Use Array#reduce method.

var arrayA = ["a", "a", "b", "b", "a", "c"];
var arrayB = [10, 20, 3, 2, 20, 5];

// reference to keep the index
var ref = {},
  // array for keeping first result
  res1 = [];


var res2 = arrayA
  // iterate over the first array
  .reduce(function(arr, v, i) {
    // check index presenet in the refernece object
    if (!(v in ref)) {
      // if not then define the index and insert  0 in the array(defining the new index)
      arr[ref[v] = arr.length] = 0;
      // push value into the array( for unique value )
      res1.push(v);
    }
    // update the element at the index
    arr[ref[v]] += arrayB[i];
    // return the array reference
    return arr;
    // initialize initial value as an empty array to keep result
  }, [])

console.log(res1, res2);

You can compute sums of all elements in arrayB that corresponds to each element in arrayA, store these sums in an object and use Object.values to get an array of the sums.

var arrayA = ["a", "a", "b", "b", "a", "c"];
var arrayB = [10, 20, 3, 2, 20, 5];

var sum = {};

arrayA.forEach((l, index) => {
    sum[l] = (sum[l] || 0) + arrayB[index];
});

var res = Object.values(sum);

console.log(res);

And it can be done even shorter with array.prototype.reduce:

var arrayA = ["a", "a", "b", "b", "a", "c"];
var arrayB = [10, 20, 3, 2, 20, 5];

var res = Object.values(arrayA.reduce((m, l, index) => {
    m[l] = (m[l] || 0) + arrayB[index];
    return m;
}, {}));

console.log(res);

With an intermediary "result" object:

var arrayA = ["a", "a", "b", "b", "a", "c"];
var arrayB = [10, 20, 3, 2, 20, 5];
var result = {};

for (var i = 0, max = arrayA.length; i < max; i++) {
    if (!result[arrayA[i]]) {
        result[arrayA[i]] = 0;
    }

    result[arrayA[i]] += arrayB[i];
}

var keys = Object.keys(result);

arrayA = [];
arrayB = [];
for (var i = 0, max = keys.length; i < max; i++) {
    arrayA.push(keys[i]);
    arrayB.push(result[keys[i]]);
}
let _ = require('underscore');

var arrayA = ["a", "a", "b", "b", "a", "c"];
var arrayB = [10, 20, 3, 2, 20, 5];

let res = {};
_.each(arrayA, (item, key) => {

  if (! res[item]) {
    res[item] = arrayB[key];
  } else {
    res[item] = res[item] + arrayB[key];
  }

});

arrayA = [];
arrayB = [];

_.each(res,(value,key) => {
  arrayA.push(key);
  arrayB.push(value);
});

console.log(arrayA);
console.log(arrayB);

First fill the dict and then fill de arrays with the keys and values

let arrayA = ["a", "a", "b", "b", "a", "c"];
let arrayB = [10, 20, 3, 2, 20, 5];

let result = {};

for (let i=0; i < arrayA.length; i++ ){
    let valueB = 0;
    if (arrayB.length > i) {
        valueB = arrayB[i];
    }

    if (result.hasOwnProperty(arrayA[i])) {
      result[arrayA[i]] += valueB;
    }
    else {
      result[arrayA[i]] = valueB;
    }
}

let resultA = [];
let resultB = [];
for (let k in result) {
    resultA.push(k);
    resultB.push(result[k]);
}
console.log(resultA);
console.log(resultB);
Related