AlvMF1
AlvMF1

Reputation: 170

JavaScript 2D array grouping: is there a more elegant/optimized way?

I have the following code which works just fine. I'm just wondering if there's a more elegant/optimized way of doing so.

Long "code" short, given the data:

var myArray = [
  ['X', 'A', 0, 1, 'Y', 3],
  ['X', 'A', 4, 5, 'Y', 6],
  ['X', 'B', 6, 5, 'Y', 4],
  ['X', 'B', 3, 2, 'Y', 1],
  ['X', 'C', 7, 8, 'Y', 9],
];

Say I want to group by column index 1 and only sum column indexes 2, 3, 5.

Expected result is:

[
  ["A", 4, 6, 9],
  ["B", 9, 7, 5],
  ["C", 7, 8, 9]
]

// data
var myArray = [
  ['X', 'A', 0, 1, 'Y', 3],
  ['X', 'A', 4, 5, 'Y', 6],
  ['X', 'B', 6, 5, 'Y', 4],
  ['X', 'B', 3, 2, 'Y', 1],
  ['X', 'C', 7, 8, 'Y', 9],
];

// col that I want to group by
var colIndex = 1;

// cols I want to sum
var colsToSum = [2, 3, 5];

var arrayGroupBy = function(myArray, colIndex, colsToSum) {

  // get unique column values
  var uniqueValues = [];
  myArray.forEach(function(row) {
    if (uniqueValues.indexOf(row[colIndex]) === -1) {
      uniqueValues.push(row[colIndex]);
    }
  });

  var newData = [];
  uniqueValues.forEach(function(value) {

    // get filtered rows
    var filteredRows = myArray.filter(function(row) {
      return row[colIndex] === value;
    });

    var row = [value];

    // columns to sum
    colsToSum.forEach(function(num) {
      if (filteredRows.length === 1) {

        // push single row
        row.push(filteredRows[0][num]);

      } else {

        // sum row cols
        var total = filteredRows.reduce(function(sum, current) {
          return sum + current[num];
        }, 0);

        row.push(total);
      }

    });

    newData.push(row);
  });

  return newData;
};

console.log(arrayGroupBy(myArray, colIndex, colsToSum));

Unfortunately I can't use ES6 on this one...

Thanks!

Upvotes: 0

Views: 523

Answers (1)

name not found
name not found

Reputation: 622

I tried to find a solution for your question. There would be many good ES6 features which would make the solution a bit more readable/cleaner. But here is a solution without any ES6 features:

var groupBy = function(myArray, colIndex, colsToSum) {
  var obj = {};
  myArray.forEach(function(e) {
    if(!obj.hasOwnProperty(e)) {
      obj[e[colIndex]] = new Array(colsToSum.length + 1)
        .join('0').split('').map(parseFloat);
      }
  });

  myArray.forEach(function(row) {
    for (var i = 0; i < colsToSum.length; i++) {
      obj[row[colIndex]][i] += row[colsToSum[i]];
    }
  });

  return Object.keys(obj).map(function(key) {
    return [key].concat(obj[key]);
  });
}

Explanation:

  • An object with the properties 'A', 'B' and 'C' will be created.
  • An array [0, 0, 0] will be assigned to each property.
  • Loop myArray and colsToSum and add the values to the right object property
  • Convert the object to an array and return it

Maybe there are better solutions :)

Upvotes: 1

Related Questions