ireth92
ireth92

Reputation: 75

Average of bidimensional array's columns with array.map()

I have an array that looks like this:

var array = [ [1,3,9],
              [4,6,8],
              [3,7,5],
              [2,8,4] ];

I want to get the average number of each column, but for now I was just trying to sum them. This is my code:

var sum = function(arr) {
  return arr.reduce(function(a, b) { return a + b; }, 0);
};

var tests = array.map(function(v, i) {
  return sum(array.map(function(v) { return v[i]; }))
});

return tests;

The output turns the sum correctly, but it seems to be doing as many sums as there are rows (4 rows), instead of 3 corresponding to the columns. This is the output:

tests = [10, 24, 26, NULL]

Any idea why is this happening? How can I perform the calculation only for as many columns as there are instead of rows?

EDIT:

I'm using Nenad's answer which gives the correct result. But I need to implement it on Google Sheets's Script Editor, which doesn't seem to understand the shortened functions with "=>". I replaced the shortened pieces for the longer version, but I'm not getting the same result.

var array = [ [1,3,9],
              [4,6,8],
              [3,7,5],
              [2,8,4] ];

var sums = array.reduce(function(r, e, i) {
    e.forEach(function(a,j) { r[j] = (r[j] || 0) + a;
                             if (i == array.length-1) { r = r.map(function(el){ return el/array.length; }); }
                            });
    return r;
  }, [])
  
console.log(sums);  

I don't see any difference between this and the shortened version, yet this one returns:

sums = [0.15625, 0.75, 1.34375];

Instead of:

sums = [2.5, 6, 6.5];

The sum is done correctly, but when I divide "el/array.length" or even "el/4", the result are these 3 weird numbers. I don't understand where are those coming from. Where did I go wrong?

Upvotes: 1

Views: 345

Answers (6)

mattphillips
mattphillips

Reputation: 56

This will convert the 2d array of rows into a 2d array of columns and then maps each inner array of columns to an average. There is a little bit of boilerplate to make the inner reduce immutable you could use lodash/fp or another library to clean this up.

const array = [ 
  [1,3,9],
  [4,6,8],
  [3,7,5],
  [2,8,4] 
];

const averageColumns = array => array.reduce((acc, row) => {
  return row.reduce((accRow, col, index) => {
    const cols = accRow[index] || [];
    return [...accRow.slice(0, index), cols.concat(col), ...accRow.slice(index + 1)];

  }, acc);
}, []).map(avg);

const avg = array => array.reduce((acc, next) => acc + next, 0) / array.length;

console.log(averageColumns(array));

Upvotes: 1

Xorifelse
Xorifelse

Reputation: 7911

To get the average of each column you'd first have to know the amount of columns. Then grab each column with map() and to sum everything with reduce()

Now we have the column, the sum and then just divide by column length.

const arrayColumn = (a, n) => a.map(x => x[n]);
const arraySum    = (a) => a.reduce((b,c) => b + c);

var arr = [ 
 [1,3,9],
 [4,6,8],
 [3,7,5],
 [2,8,4] 
];

for(i=0; i<arr[0].length; i++){
  console.log(arraySum((col = arrayColumn(arr, i))) / col.length);
}

Upvotes: 1

Redu
Redu

Reputation: 26191

I would do as follows;

var array = [ [1,3,9],
              [4,6,8],
              [3,7,5],
              [2,8,4] ];
   result = array.map(a => a.reduce((p,c,_,a) => p + c/a.length,0));
console.log(result);

As per the comments... Yes they are right, the right solution should be through a switch of the .map() and .reduce();

var array = [ [1,3,9],
              [4,6,8],
              [3,7,5],
              [2,8,4] ],
   result = array.reduce((p,c) => p.map((n,i) => n + c[i]/array.length), Array(array[0].length).fill(0));
console.log(result);

Upvotes: 0

maioman
maioman

Reputation: 18764

You can transpose the array of arrays,
popular utility libraries (ramda for ex) have a transpose implementation,
but it's easy to implement your own:

const trans = arr => arr[0].map((_,i) => arr.map(x => x[i]))

var array = [ 
 [1,3,9],
 [4,6,8],
 [3,7,5],
 [2,8,4] 
];
const res = trans(array)
console.log(res)


// to get the sum you can use reduce
console.log(res.map( x => x.reduce((a,b) => a + b )))

Upvotes: 0

Dima  Lutsik
Dima Lutsik

Reputation: 301

map() + reduce() solution

var array = [ [1,3,9], [4,6,8], [3,7,5], [2,8,4] ];


array.map(function(item, i, arr) {
	arr[i] = item.reduce((a, b) => a + b, 0) / 2;
  	console.log(arr[i])
});

I'm a little fix up your code
you have mistake here return sum(array.map(function(v) { return v[i]; }))

var array = [ [1,3,9],
              [4,6,8],
              [3,7,5],
              [2,8,4] ];
function sum(arr) {
  return arr.reduce(function(a, b) { return a + b; }, 0);
};

var tests = array.map(function(v, i, arr) {
  return sum(arr[i])
});

tests;

Upvotes: 0

Nenad Vracar
Nenad Vracar

Reputation: 122087

You can use reduce() and forEach() to return result.

var array = [
  [1, 3, 9],
  [4, 6, 8],
  [3, 7, 5],
  [2, 8, 4]
];

var sums = array.reduce(function(r, e, i) {
  e.forEach((a, j) => r[j] = (r[j] || 0) + a)
  return r;
}, [])

console.log(sums)

To calculate avg for each column you can add map() on last iteration of array.

var array = [
  [1, 3, 9],
  [4, 6, 8],
  [3, 7, 5],
  [2, 8, 4]
];

var sums = array.reduce(function(r, e, i) {
  e.forEach((a, j) => r[j] = (r[j] || 0) + a)
  if (i == array.length - 1) r = r.map(el => el / array.length);
  return r;
}, [])

console.log(sums)

Upvotes: 2

Related Questions