javascript - Creează un obiect dintr-un array de obiecte după proprietăți dinamice
Am un array de obiecte
"options": [
{
"width": 10,
"height": 20
},
{
"width": 20,
"height": 40
},
{
"width": 30,
"height": 60
}
]
Că vreau să convertesc la următorul
{ width: [10, 20, 30], height: [20, 40, 60] }
Acum țineți minte că cheile sunt dinamice. În acest caz, sunt width și height.
De fapt, chiar am o soluție.
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;
Dar pot să fac asta mai scurt?
Răspuns #1
Cea mai simplă modalitate este să scrii pur și simplu un ciclu. Să începem doar cu JavaScript și apoi ne vom întoarce la tipuri:
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]);
}
}
Exemplu live:
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;
}Despre for (const key in entry) vs. for (const key of Object.keys(entry), primul vizitează și proprietățile moștenite. Exemplul tău nu are proprietăți moștenite, dar dacă ai avea, ai putea fie să adaugi o verificare hasOwnProperty, fie, dacă nu te deranjează array-ul temporar, să folosești of Object.keys(entry).
Ok, deci: Tipuri. Mă așteptam ca asta să fie destul de dificil, dar s-a dovedit că nu este, ceea ce mă face să suspectez că îmi scapă ceva. :-) Am folosi o funcție pentru asta, astfel încât să putem folosi parametrii de tip:
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);
Link către terenul de joacă
Deoarece rezultatul se construiește în timp, am folosit tipuri destul de flexibile în cadrul funcției condense, dar intrarea și ieșirea sunt corespunzător tipizate. De exemplu, rezultatul lui result de mai sus este { width: number[]; height: number[]; }, ceea ce ne dorim. Funcționează chiar și dacă adăugăm o a treia proprietate (să zicem, description) de un tip diferit (să zicem, string), dându-ne { width: number[]; height: number[]; description: string[]; }: link către Playground
Moda în prezent este să folosești reduce, dar nu există niciun motiv să îl folosești aici, tot ce face este să adauge complexitate.
Răspunsul #2
Fac cu bucla for, pentru că este mai receptivă și mai rapidă decât map sau 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)