Reputation: 919
I have an array of objects
"options": [
{
"width": 10,
"height": 20
},
{
"width": 20,
"height": 40
},
{
"width": 30,
"height": 60
}
]
That I want convert to the following
{ width: [10, 20, 30], height: [20, 40, 60] }
Now keep in mind that the keys are dynamic. In this instance it's width
and height
.
I do actually have solution.
const pluck = (arr: Record<string, any> | undefined, property: string) => {
return arr?.map((obj: any) => {
return obj[property];
});
};
const unique = (arr: any[] | undefined) =>
arr ? Object.keys(Object.assign({}, ...arr)) : null;
const keys = unique(myArray);
const options = keys
? Object.fromEntries(keys.map((key) => [key, pluck(myArray, key)]))
: null;
But can I make this shorter?
Upvotes: 3
Views: 1708
Reputation: 1075905
The simplest way is just to write a loop. Let's start with just JavaScript and then we'll come back to types:
const result = {};
for (const entry of myArray) {
for (const key in entry) { // or: of Object.keys(entry)
const values = result[key] = result[key] ?? [];
values.push(entry[key]);
}
}
Live Example:
const myArray = [
{
"width": 10,
"height": 20
},
{
"width": 20,
"height": 40
},
{
"width": 30,
"height": 60
}
];
const result = {};
for (const entry of myArray) {
for (const key in entry) { // or: of Object.keys(entry)
const values = result[key] = result[key] ?? [];
values.push(entry[key]);
}
}
console.log(result);
.as-console-wrapper {
max-height: 100% !important;
}
Re for (const key in entry)
vs. for (const key of Object.keys(entry)
, the former also visits inherited properties. Your example doesn't have any inherited properties, but if you had them, you could either add a hasOwnProperty
check, or if you don't mind the temporary array, use of Object.keys(entry)
.
Okay, so: Types. I was expecting this to be quite difficult, but it turned out not to be, which makes me suspicious I'm missing something. :-) We'd use a function for it so we can use type parameters:
function condense<ObjType extends object>(array: ObjType[]) {
const result: Record<string, any> = {};
for (const entry of array) {
for (const key in entry) { // or: of Object.keys(entry)
const values = result[key] = result[key] ?? [];
values.push(entry[key]);
}
}
return result as {[key in keyof ObjType]: ObjType[key][]};
}
const result = condense(myArray);
console.log(result);
Since the result is built up over time, I used fairly loose types within the condense
function, but the input and output are properly typed. For instance, the result of result
above is { width: number[]; height: number[]; }
, which is what we want. It even works if we throw in a third property (say, description
) of a different type (say, string
), giving us { width: number[]; height: number[]; description: string[]; }
: Playground link
The fad at present is to use reduce
, but there's no reason to use it here, all it does is add complexity.
Upvotes: 3
Reputation: 422
i do with for loop, because its more responsive and faster than map or reduce.
const options = [
{ "width": 10, "height": 20 },
{ "width": 20, "height": 40 },
{ "width": 30, "height": 60 }
];
//That I want convert to the following
//{ width: [10, 20, 30], height: [20, 40, 60] }
let newobj = new Object();
for( let a of options ){
for( let b of Object.keys(a) ){
if( !newobj[b] ) newobj[b]=[]; newobj[b].push(a[b]);
}
}
console.log(newobj)
Upvotes: 0
Reputation: 5
var height = [], width = [];
options.forEach((op) =>{
height.push(op.height)
width.push(op.width)
})
var returnValue = {width: width, height: height}
Upvotes: -1
Reputation: 5797
See Array.prototype.reduce()
for additional info.
const input = [
{ width: 100, height: 100 },
{ width: 200, height: 200 },
{ width: 300, height: 300 },
];
const group = (someArrayOfObjects) => {
return someArrayOfObjects.reduce((acc, current) => {
Object.entries(current).forEach(([key, value]) => {
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(value);
});
return acc;
}, {});
};
console.log(group(input))
Upvotes: -1