I have array of object data and I want to dynamically convert them to object with key and array. How to make make keys of objects to work dynamically in typescript?
For example if the data be like:
data1 = [
{a: 'st1', b: 1, c: 1, d: 1, e: 'e1' },
{a: 'st2', b: 2, c: 2, d: 2, e: 'e2'},
{a: 'st3', b: 3, c: 3, d: 3, e: 'e3'},
{a: 'st4', b: 4, c: 4, d: 4, e: 'e4' },
]
and I it to convert to:
data2= {
a: ['st1', 'st2', 'st3', 'st4',]
b: [1, 2, 3, 4,],
c: [1, 2, 3, 4,],
d: [1, 2, 3, 4,],
e: ['e1', 'e2', 'e3', 'e4']
}
The simplest solution is to
type Data2Props = {
a: string[],
b: number[],
c: number[],
d: number[],
e: string[]
}
const data2: Data2Props = {
a: [],
b: [],
c: [],
d: [],
e: []
}
data1?.forEach((item) => {
data2.a.push(item.a)
data2.b.push(item.b)
data2.c.push(item.c)
data2.d.push(item.d)
data2.e.push(item.e)
})
But what if the number of keys increase, is there any better and more concise solution?
Something like this will work in javascript but not in typescript.
let keys: string[] = ['a', 'b', 'c', 'd', 'e'];
let data2 = {}
keys.forEach((key) => data2[key] = [])
data1?.forEach((item) => {
keys.forEach((key) => data2[key].push(item[key])
}
In typescript it will return an error of typing.
CodePudding user response:
You can do something like below in typescript
let data1:{[key:string]:any}[] = [
{a: 'st1', b: 1, c: 1, d: 1, e: 'e1' },
{a: 'st2', b: 2, c: 2, d: 2, e: 'e2'},
{a: 'st3', b: 3, c: 3, d: 3, e: 'e3'},
{a: 'st4', b: 4, c: 4, d: 4, e: 'e4' },
];
let keys: string[] = ['a', 'b', 'c', 'd', 'e'];
let data2:{[key:string]:any[]} = {}
keys.forEach((key) => data2[key] = [])
data1?.forEach((item) => {
keys.forEach((key) => data2[key].push(item[key]))
});
console.log(data2);
Or even without defining keys array
let data1:{[key:string]:string|number}[] = [
{a: 'st1', b: 1, c: 1, d: 1, e: 'e1' },
{a: 'st2', b: 2, c: 2, d: 2, e: 'e2'},
{a: 'st3', b: 3, c: 3, d: 3, e: 'e3'},
{a: 'st4', b: 4, c: 4, d: 4, e: 'e4' },
] ;
let data2:{[key:string]:any[]}={};
data1.forEach(obj=>{
Object.keys(obj).forEach(key=>{
let val=obj[key];
if(data2[key]){
data2[key].push(val);
}else{
data2[key]=[val];
}
})
});
console.log(data2)
CodePudding user response:
The approach below requires a bunch of type assertions inside the transpose
function, but usage of the function is entirely type-safe:
const ITEM = {
a: '',
b: 0,
c: 0,
d: 0,
e: '',
}
type Item = typeof ITEM
type TransposedItems = {
[Key in keyof Item]: Item[Key][]
}
function transpose(items: Item[]): TransposedItems {
const transposed: Partial<TransposedItems> = {}
for (const key in ITEM) {
transposed[key as keyof Item] = items.map(item => item[key as keyof Item]) as any[]
}
return transposed as TransposedItems
}
const data1: Item[] = [
{a: 'st1', b: 1, c: 1, d: 1, e: 'e1' },
{a: 'st2', b: 2, c: 2, d: 2, e: 'e2'},
{a: 'st3', b: 3, c: 3, d: 3, e: 'e3'},
{a: 'st4', b: 4, c: 4, d: 4, e: 'e4' },
]
console.log(transpose(data1))
The trick is to define a constant ITEM
with the same type as the actual elements, and let type inference do its work on it. Because ITEM
exists at runtime, we can iterate over its keys, something that is difficult to do if it were only a type that existed at compile time.
You could also just use the first item in the array for this, but you'll run into trouble if the objects aren't all exactly the same shape. Also, that only works if the array is not empty.