How to multiply certain columns/fields in a large array of objects?

Viewed 305

I have an array as the following:

const data = [
  { Date: "2012", A: 2,   B: 3,   C: 4 },
  { Date: "2013", A: 4,   B: 7,   C: 8 },
  { Date: "2014", A: 0.1, B: 0.3, C: 0.4 },
];

I want to multiply specific columns/fields by 100, so I created a new array that holds their keys:

const arr = ["A", "B", "C"];

And then I use .map to multiple those columns:

arr.forEach((element, i) => data.map(a => a[element] * 100));

However, when I check the data array of objects it seems to be unchanged:

const data = [
  { Date: "2012", A: 2,   B: 3,   C: 4 },
  { Date: "2013", A: 4,   B: 7,   C: 8 },
  { Date: "2014", A: 0.1, B: 0.3, C: 0.4 },
];

const arr = ["A", "B", "C"];

arr.forEach((element, i) => data.map(a => a[element] * 100));

console.log(data);

7 Answers

You could combine map and reduce to create new array of objects and keep the original data.

var data = [{Date:"2012", A:2, B:3, C:4}, {Date:"2013", A:4, B:7, C:8},  {Date:"2014", A:0.1, B:0.3, C:0.4}]
var arr = ["A","B","C"]

// map over data array and return a new object for each element
const result = data.map(o => ({
  // spread current object from original array
  ...o,
  // spread object returned from reduce that will overide original properties
  ...arr.reduce((r, k) => {
  // check if the current key ('A', 'B'...) exists in current object
  // and if it does create new property where the value is
  // original value * 100
    if (o[k]) r[k] = o[k] * 100;
  // return accumulator of reduce
    return r;
  }, {})
}))

console.log(result)

You are not assigning the new values.

If you want to mutate the original elements, then instead of a[element] * 100 you should have a[element] = a[element] * 100 or a[element] *= 100.

If you don't want to mutate the original elements, then you should be assigning back the value map returns and creating new objects:

arr.forEach((element, i) => data = data.map(a => { 
  return { ...a, [element]: a[element] * 100 };
}));

If you are concerned about performance, you can use performance.now() to check how much time it takes to run different variations of your code:

const columns = ["A", "B", "C"];

let data1 = [];
const data2 = [];
let data3 = [];
const data4 = [];

for (let i = 0; i < 100000; ++i) {
  data1.push({ Date: "2012", A: 2, B: 3, C: 4 });
  data2.push({ Date: "2012", A: 2, B: 3, C: 4 });
  data3.push({ Date: "2012", A: 2, B: 3, C: 4 });
  data4.push({ Date: "2012", A: 2, B: 3, C: 4 });
}

// Original code with map (doesn't mutate original rows):

const t1 = performance.now();

columns.forEach(column => data1 = data1.map(row => { 
  return { ...row, [column]: row[column] * 100 };
}));

console.log(Object.values(data1[0]).join(', '), performance.now() - t1);

// Original code with assignment (no map, mutates original rows):

const t2 = performance.now();

columns.forEach(column => data2.forEach(row => row[column] *= 100));

console.log(Object.values(data2[0]).join(', '), performance.now() - t2);

// Iteraring rows first with map (doesn't mutate original rows):

const t3 = performance.now();

data3 = data3.map((row, i) => {
  const newRow = { ...row };
  
  columns.forEach(column => newRow[column] *= 100);
  
  return newRow;
});

console.log(Object.values(data3[0]).join(', '), performance.now() - t3);

// Iteraring rows first with assignment (no map, mutates original rows):

const t4 = performance.now();

data4.forEach((row, i) => columns.forEach(column => row[column] *= 100));

console.log(Object.values(data4[0]).join(', '), performance.now() - t4);

Also, you might want to consider using a for or while loop, which might be faster than .map or .forEach.

You'll need to loop through the data, then for each obj, alter each arr key

let data = [{Date:"2012", A:2, B:3, C:4}, {Date:"2013", A:4, B:7, C:8},  {Date:"2014", A:0.1, B:0.3, C:0.4}]
var arr = ["A","B","C"]

data.forEach((obj) => arr.forEach((a) => obj[a] *= 100));
console.log(data);

map returns a new array, it doesn't mutate the original array it iterates over.

Array.map returns a new array, it won't mutate in place.

You could do this:

 arr.forEach((element, i) => data.forEach((a,i)=>data[i].a[element]*100))

... but I wouldn't recommend it.

Mutating an object that you're iterating over is a Bad Idea, much better to map into a new array.

This is because the map function creates a new Array. if you grab the output you will see that it is calculated and put into a new array (just with the output variables)

var data = [{Date:"2012", A:2, B:3, C:4}, {Date:"2013", A:4, B:7, C:8},  {Date:"2014", A:0.1, B:0.3, C:0.4}]
var arr = ["A","B","C"]
arr.forEach((element, i) =>  {
  var output = data.map(a=>a[element]*100)
  console.log(output);
}
)

OUTPUTS

[200, 400, 10]
[300, 700, 30]
[400, 800, 40]

https://jsfiddle.net/8ksL3pn0/

It should be like this:

arr.forEach((element, i) => data.map(a=>{a[element]*=100}))

Related