JavaScript, split object in 2, and push in array - javascript

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

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

array of objects manipulation in js

I have an array of object as below.
data: [ {col: ['amb', 1, 2],} , {col: ['bfg', 3, 4], },]
From above, I need to get array of array as below.
[ [{a: 'amb',b: [1], c: 'red'}, {a: 'amb',b: [2], c: 'orange'}],
[{a: 'bfg',b: [3], c: 'red'}, {a: 'bfg',b: [4], c: 'orange'}]
]
My attempt is as below.
let arrInner: Array<any> = []
let arrOuter: Array<Array<any>> = []
_.forEach(data, (item, i) => {
//create two objects redObj and orangeObj
redObj = {
a: item.col[0].toString(),
b: [item.col[1] as number],
c: 'red'
}
orangeObj = {
a: item.col[0].toString(),
b: [item.col[2] as number],
c: 'orange'
}
//put those two objects to array
arrInner.push(redObj)
arrInner.push(orangeObj)
//assign that array to another array
arrOuter[i] = arrInner
})
But when I print the arrOuter, it is not my expected output. Where I was wrong and how can I fix this?
You need to create a new arrInner each time through the forEach loop. Then push that onto arrOuter.
let arrOuter: Array <Array <any>> = []
_.forEach(data, (item, i) => {
//create two objects redObj and orangeObj
redObj = {
a: item.col[0].toString(),
b: [item.col[1] as number],
c: 'red'
}
orangeObj = {
a: item.col[0].toString(),
b: [item.col[2] as number],
c: 'orange'
}
//put those two objects to array
let arrInner = [redObj, orangeObj]
//assign that array to another array
arrOuter.push(arrInner)
})

ES6 spread syntax : skip empty keys automatically?

What is simpler pattern to avoid updating inputs if one of the keys is empty in payload ?
Is there a nice ES6 syntax ?
const master = {
inputs: {a: [], b: [], c: []}
};
const {a, b, c} = payload;
const updateMaster = (payload) => ({
...master, inputs: {...master.inputs, ...payload}
});
To filter the fields of an object, use Object.entries to retrieve the fields, Array.prototype.filter to filter then, and Object.formEntries to reconstruct an object from the filtered entries.
let payload = {
a: [],
b: [1, 2]
};
let nonEmptyPayload = Object.fromEntries(Object.entries(payload).filter(([_, v]) => v.length))
console.log(nonEmptyPayload);
Applying this to your example,
let master = {
inputs: {
a: [],
b: [13, 14],
c: [10, 12]
}
};
let trimObj = obj => Object.fromEntries(Object.entries(obj).filter(([_, v]) => v.length));
let updateMaster = payload => ({
...master,
inputs: { ...master.inputs,
...trimObj(payload)
}
});
updateMaster({
b: [15, 16], // Will override master.c
c: [] // Will not override master.c
});
console.log(master);
You could create a function like this. It removes all empty values from an object, without directly modifying the object passed to the function.
const removeEmpty = obj => {
return Object.keys(obj).reduce((acc, key) => {
// value is "falsey" or is empty array
return !obj[key] || (Array.isArray(obj[key]) && !obj[key].length)
? acc
: {...acc, [key]: obj[key]}
}, {})
}
console.log(removeEmpty({a: 'AAA', b: '', c: 'CCC', d: false, e: null, f: [1,2], g: []}))
So your final snippet would look like this:
const updateMaster = (payload) => ({
...master, inputs: {...master.inputs, ...removeEmpty(payload)}
});

Create new object based on filtered keys

Suppose I have an array of strings that represent keys such as ['a', 'b', 'd'], and an existing object such as...
const obj = {
a: 1,
b: 2,
c: 3,
d: 4,
e: 5
}
Is there a method of creating a new object that is a filtered version of obj based on the keys in the array such that...
const updated = {
a: 1,
b: 2,
d: 4
}
using the Object.assign() function?
I know it works with a function such as...
function createNew(o, keys) {
const updated = {}
Object.keys(o).forEach(k => {
if (keys.includes(k)) updated[k] = o[k]
})
return updated
}
but I'm looking for a solution with Object.assign()
const obj = {
a: 1,
b: 2,
c: 3,
d: 4,
e: 5
};
const desiredKeys = ['a', 'c', 'd'];
const result = desiredKeys.reduce((acc, key) => {
acc[key] = obj[key];
return acc;
}, {});
console.log(result);

Categories

Resources