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

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);

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 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'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

Reordering an object to the keys are in the same order as an array

I have an array that looks like this,
['event_tag', 'workflow_tag', 'created_timestamp', 'success']
and an array of objects where the object looks like,
{
"created_timestamp": "2022-04-01T13:14:53.028002Z",
"workflow_tag": "dj807",
"event_tag": "refresh",
"success": true
}
What I am wanting to do is make the above object and any other objects in that array match the order of the values in the first array so the finished object should look like,
{
"event_tag": "refresh",
"workflow_tag": "dj807",
"created_timestamp": "2022-04-01T13:14:53.028002Z",
"success": true
}
I have tried the following so far,
const keys = ['event_tag', 'workflow_tag', 'created_timestamp', 'success'];
newRowData = parsedRows.reduce((obj, v) => {
obj[v] = keys[v];
return obj
}, {});
But this returns,
{[object Object]: undefined}
You could order the keys by constructing a new object inside of an Array#map:
const parsedRows = [ { a: 1, c: 3, d: 4, b: 2, }, { b: 6, a: 5, c: 7, d: 8, }, { d: 12, b: 10, a: 9, c: 11, }, ];
const order = ['a', 'b', 'c', 'd'];
let newData = parsedRows.map(row => {
let newRow = {};
for (let key of order) {
newRow[key] = row[key];
}
return newRow;
});
console.log(newData);
Instead of iterating over Rows, Iterate on keys either map/reduce.
const keys = ["event_tag", "workflow_tag", "created_timestamp", "success"];
const obj = {
created_timestamp: "2022-04-01T13:14:53.028002Z",
workflow_tag: "dj807",
event_tag: "refresh",
success: true,
};
const res = Object.assign({}, ...keys.map((key) => ({ [key]: obj[key] })));
console.log(res)

Best way to create an array from an object that has number of copies

From an object like this:
{a:1, b: 2, c: 3}
I would like to turn into
['a', 'b', 'b', 'c', 'c', 'c']
Where the key is the string and the value is the number of copies, order doesn't matter.
What's the best way to do this?
I was thinking about using array.fill but not sure if that's actually easier than just iterating and push.
Edit: Currently this:
const arr = []
_.each(obj, function (v, k) {
_.times(v, function () {
arr.push(k)
})
})
You could flatMap the Object.entries and fill an array of each size.
const obj = { a: 1, b: 2, c: 3 };
const result = Object.entries(obj).flatMap(([k, v]) => Array(v).fill(k));
console.log(result)
or with Lodash
const obj = { a: 1, b: 2, c: 3 };
const arr = _.flatMap(obj, (v,k) => Array(v).fill(k))
console.log(arr);
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.21/lodash.min.js"></script>
But there's nothing like a simple loop
const obj = { a: 1, b: 2, c: 3 };
const result = []
for (let [k, v] of Object.entries(obj)) {
while (v--) {
result.push(k)
}
}
console.log(result)
I would convert the object into an array of keys using Object.keys and then use a newly created empty results array, then map through the keys.
For each key I would add a fill array to the existing results.
Here's the ES6 solution to that (no extra libraries required)
const obj = { a: 1, b: 2, c: 3 };
let result = []
Object.keys(obj).forEach(key => {
result = [...result, ...new Array(obj[key]).fill(key)]
})
console.log(result)
You can use Object.entries and Array#reduce as follows:
const input = {a:1, b: 2, c: 3};
const output = Object.entries(input).reduce(
(prev, [key,value]) => prev.concat( Array(value).fill(key) ),
[]
);
console.log( output );
Or, using Array#push instead of Array#concat,
const input = {a:1, b: 2, c: 3};
const output = Object.entries(input).reduce(
(prev, [key,value]) => prev.push( ...Array(value).fill(key) ) && prev,
[]
);
console.log( output );
Or, using for loops,
const input = {a:1, b: 2, c: 3};
const output = [],
pairs = Object.entries(input);
for(let i = 0; i < pairs.length; i++) {
const [key, value] = pairs[i];
for(let j = 0; j < value; j++) {
output.push( key );
}
}
console.log( output );

JavaScript, split object in 2, and push in array

I have an array of objects like this:
const arrayOfObjects = [
{ A: 1, B: 2, C: 3 },
{ A: 3, B: 4, C: 1 }
]
And another array which is called "headers"
const headers = [
['A', 'B'],
['C']
]
I have to create an array similar to the first one but, with those objects splited by what headers have in it's arrays.
This should be the goal:
const result = [
[
{ A: 1, B: 2 },
{ C: 3 }
],
[
{ A: 3, B: 4 },
{ C: 1 }
]
]
I tried by doing a "base" array with:
const baseArray = []
headers.forEach((header) => {
const objFromHeader = {};
header.forEach((head) => {
objFromHeader[head] = 0;
});
baseArray.push(objFromHeader);
});
That will give me the result array but with 0 values for each key.
And then loop for the first array and put inside another array the base array with the correct values.
Then I wanted to fill each key according to the value that comes from arrayOfObjects but here is where I can't see how could I loop that array of objects and put the correct value. The only problem with that approach is that the result array will have some 0 values that come from the initiation array that I'm using, it would be better to me to only put the objects that actually have values and not 0 (I was thinking on another function to delete those keys with value = 0...)
How could I achieve it in a better way?
Fiddle:
https://jsfiddle.net/pmiranda/Lpscz6vt/
When iterating over an object, use findIndex on the headers to identify which index in the headers array the property being iterated over should go into. Create an object there if it doesn't exist yet, and set the property.
const arrayOfObjects = [
{ A: 1, B: 2, C:3 },
{ A: 3, B: 4, C:1 }
];
const headers = [
['A', 'B'],
['C']
];
const toResultItem = (object) => {
const resultItem = [];
for (const [key, value] of Object.entries(object)) {
const headersIndex = headers.findIndex(arr => arr.includes(key));
resultItem[headersIndex] ??= {};
resultItem[headersIndex][key] = value;
}
return resultItem;
};
console.log(arrayOfObjects.map(toResultItem));
const arrayOfObjects = [
{ A: 1, B: 2, C: 3 },
{ A: 3, B: 4, C: 1 },
];
const headers = [['A', 'B'], ['C', 'D']];
const result = arrayOfObjects.map((obj) =>
headers.map((header) =>
header.reduce((acc, key) => {
acc[key] = obj[key];
return Object.keys(acc).reduce((newAcc, key) => {
if (acc[key]) {
newAcc[key] = acc[key];
}
return newAcc;
}
, {});
}, {})
)
);
console.log(result);
Array.forEach implementation
Logic
Loop through arrayOfObjects array.
Inside that, loop through headers array.
Inside that, loop through each array in the headers array.
Create an empty object and assign the property from nodes in headers array with values from objects in arrayOfObjects array.
const arrayOfObjects = [
{ A: 1, B: 2, C: 3 },
{ A: 3, B: 4, C: 1 }
];
const headers = [
['A', 'B'],
['C']
];
const baseArray = []
arrayOfObjects.forEach((obj) => {
const childNode = [];
headers.forEach((head) => {
const node = {};
head.forEach((key) => node[key] = obj[key]);
childNode.push(node);
});
baseArray.push(childNode);
});
console.log(baseArray)
Array.map and Array.reduce implementation.
Using the same logic implementes in the above solution, we can rewrite this using Array.map and Array.reduce as below.
const arrayOfObjects = [
{ A: 1, B: 2, C: 3 },
{ A: 3, B: 4, C: 1 }
];
const headers = [
['A', 'B'],
['C']
];
const output = arrayOfObjects.map((obj) => {
return headers.map((header) => {
return header.reduce((acc, curr) => {
acc[curr] = obj[curr];
return acc;
}, {});
})
})
console.log(output);

make function take multiple variables from an array passed in as parameter

How to make function take multiple variables from an array passed in as parameter?
Edited
For example:
Achieve this:
const inputObj = [
['Anna', 10, 'Monday'],
['Anna', 15, 'Wednesday'],
['Beatrice', 8, 'Monday'],
['Beatrice', 11, 'Wednesday'],
['Anna', 4, 'Wednesday'],
['Beatrice', 5, 'Monday'],
['Beatrice', 16, 'Monday']
]
// expected output:
const outputObj = [
[ 'Anna', 10, 'Monday' ],
[ 'Anna', 19, 'Wednesday' ],
[ 'Beatrice', 29, 'Monday' ],
[ 'Beatrice', 11, 'Wednesday' ]
]
const arr = [0, 2]
const someFunction = (obj, v, a) => {
const result = obj.reduce((acc, cur) => {
const key = `${cur[a[0]]}|${cur[a[1]]}`
if(!acc[key]) acc[key] = cur
else acc[key][1] += cur[v]
return acc
}, {})
return Object.values(result)
}
console.log(someFunction(inputObj, 1, arr))
with this:
const arr = [0, 2, 3, ...] // basically the array could contain any number of items.
const someFunction = (obj, v, objParams) => {
const result = obj.reduce((acc, cur) => {
const key = ???
...
}, {})
}
So that the function can be reused and it accepts custom-sized arrays, check if the column numbers in the array are the same, then adds the sum of the column that is passed in as v?
How to declare the variables from the objParams to achieve the same result as the code above does?
Also how to add v in the middle of cur?
Assuming objParams is an array of unknown size (strings in this example):
const objParams = ["c1", "c2", "c3"];
const key = objParams.join(']}|${cur[');
const built = '${cur[' + key + ']';
Built is:
${cur[c1]}|${cur[c2]}|${cur[c3]
With ES6 you can use the spread operator in the argument definition.
More reading about spread operator on MDN
function sum(...args) {
return args.reduce((result, value) => result + value, 0)
}
const numbers = [1, 2, 3];
console.log('sum', sum(2, 2));
console.log('sum', sum(...numbers));
console.log('sum', sum(1, 2, 1, ...numbers));
// get single args before accumulating the rest
function sum2(foo, bar, ...args) {
return args.reduce((result, value) => result + value, 0)
}
console.log('sum2', sum2(2, 2));
console.log('sum2', sum2(...numbers));
console.log('sum2', sum2(1, 2, 1, ...numbers));

How can I get object from another object, only with needed keys?

I have one strange question about Destructuring in the JS. How can I get object from another object, only with needed keys.
For example, to transform this object:
let firstObj = {
a: 1,
b: 2,
c: 3,
d: 4
};
To this:
let secondObj = {
a: 1,
d: 4
};
I have array with first type objects. For some reasons I should to use minimal code for this.
My minimum code:
arrayObj = [firstObj, firstObj, firstObj, firstObj, firstObj];
let secondArr = arrayObj.map(
({a, d}) => ({a, d})
);
How can I improve this one?
You could use an IIFE for takeing the wanted properties and return a new object.
var first = { a: 1, b: 2, c: 3, d: 4 },
second = (({ a, d }) => ({ a, d }))(first);
console.log(second);
With an array of keys for a dynamic approach
var first = { a: 1, b: 2, c: 3, d: 4 },
keys = ['a', 'd']
second = Object.assign(...keys.map(k => ({ [k]: first[k] })));
console.log(second);
You can create a array with your needed keys and use reduce() on that array to get values from firstObj.
DEMO
const neededKeys = ['a', 'd'],
firstObj = {a: 1,b: 2,c: 3,d: 4};
let result = neededKeys.reduce((r, v) => Object.assign(r,{[v]:firstObj[v]}), {});
console.log(result);
.as-console-wrapper {max-height: 100% !important;top: 0;}

Categories

Resources