Reputation: 817
Is there a way to nicely convert from an object:
Key_1 : {Head_1 : "val_11", Head_2 : "val_21", Head_3 : "val_31"}
Key_2 : {Head_1 : "val_12", Head_2 : "val_22", Head_3 : "val_32"}
Key_3 : {Head_1 : "val_13", Head_2 : "val_23", Head_3 : "val_33"}
to:
Head_1 : {Key_1 : "val_11", Key_2 : "val_12", Key_3: "val_13"}
Head_2 : {Key_1 : "val_21", Key_2 : "val_22", Key_3: "val_23"}
Head_3 : {Key_1 : "val_31", Key_2 : "val_32", Key_3: "val_33"}
In TypeScript?
Thanks a lot!
Upvotes: 0
Views: 1034
Reputation: 4184
You can use "for..of" and "Object.entries" like below to transpose
BTW, it's always better to write your approach/what have you tried in OP so that fellow users see where you are heading towards
var obj = {
Key_1 : {Head_1 : "val_11", Head_2 : "val_21", Head_3 : "val_31"},
Key_2 : {Head_1 : "val_12", Head_2 : "val_22", Head_3 : "val_32"},
Key_3 : {Head_1 : "val_13", Head_2 : "val_23", Head_3 : "val_33"}
}
function transpose(obj) {
let newObj = {}
for(let [key, value] of Object.entries(obj)) {
for(let [k, v] of Object.entries(value)) {
newObj[k] = newObj[k] || {}
newObj[k][key] = v
}
}
return newObj
}
console.log(transpose(obj))
Upvotes: 3
Reputation: 329013
The other answers are great but I don't see anything that deals with the type system specific to TypeScript. The following type definitions and signature should allow the compiler to understand the type of the transposed object (assuming TS2.8+):
type AllKeys<T> = T extends any ? keyof T : never;
type RelevantKeys<T, K extends keyof T[keyof T]> = {
[L in keyof T]: K extends keyof T[L] ? L : never
}[keyof T];
type Transpose<T> = {
[K in AllKeys<T[keyof T]>]: {
[L in RelevantKeys<T, K>]: T[L][K]
}
};
function transpose<T extends object & Record<keyof T, object>>(t: T): Transpose<T>;
// one possible implementation, use your favorite from above
function transpose(t: { [k: string]: { [l: string]: any } }) {
const ret = {} as { [l: string]: { [k: string]: any } };
Object.keys(t).forEach(k =>
Object.keys(t[k]).forEach(l => {
if (!(l in ret)) ret[l] = {};
ret[l][k] = t[k][l];
})
);
return ret;
}
Observe the types:
var obj = {
Key_1: { Head_1: "val_11", Head_2: "val_21", Head_3: "val_31" },
Key_2: { Head_1: "val_12", Head_2: "val_22", Head_3: "val_32" },
Key_3: { Head_1: "val_13", Head_2: "val_23", Head_3: "val_33" }
}
const transposed = transpose(obj);
// the following are compile-time IntelliSense and/or error messages
transposed.Head_1; // {Key_1: string, Key_2: string, Key_3: string};
transposed.Haed_2; // error, property Haed2 does not exist
transposed.Head_3.Key_2; // string
const x = { a: { b: 1, c: true }, d: { e: "hey", f: undefined } };
const y = transpose(x);
y.e.d; // string
y.e.a; // error, property 'a' does not exist on {d: string}
y.b.a; // number
y.b.d; // error, property 'd' does not exist on {a: number}
Hope that helps!
Upvotes: 2
Reputation: 44979
Another approach would be to use Array.prototype.reduce
:
const input = {
Key_1: {Head_1: 'val_11', Head_2: 'val_21', Head_3: 'val_31'},
Key_2: {Head_1: 'val_12', Head_2: 'val_22', Head_3: 'val_32'},
Key_3: {Head_1: 'val_13', Head_2: 'val_23', Head_3: 'val_33'},
};
const output = Object.entries(input).reduce((acc, [key, keyVal]) => {
Object.entries(keyVal).forEach(([head, headVal]) => {
acc[head] = acc[head] || {};
acc[head][key] = headVal;
});
return acc;
}, {});
console.log(output);
Don't forget to transpile and add polyfills if required for compatibility.
Upvotes: 0