javascript - Membuat objek dari array objek berdasarkan properti dinamis
Saya memiliki sebuah array objek
"options": [
{
"width": 10,
"height": 20
},
{
"width": 20,
"height": 40
},
{
"width": 30,
"height": 60
}
]
Bahwa saya ingin mengubah menjadi berikut
{ width: [10, 20, 30], height: [20, 40, 60] }
Sekarang perlu diingat bahwa kunci-kuncinya bersifat dinamis. Dalam hal ini adalah width dan height.
Sebenarnya saya memang punya solusi.
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;
Tapi bisakah saya membuat ini lebih pendek?
Jawaban #1
Cara termudah adalah dengan menulis sebuah loop. Mari kita mulai dengan hanya JavaScript dan kemudian kita akan kembali ke tipe-tipe:
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]);
}
}
Contoh Langsung:
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;
}Mengenai for (const key in entry) vs. for (const key of Object.keys(entry), yang pertama juga mengunjungi properti yang diwarisi. Contoh Anda tidak memiliki properti yang diwarisi, tetapi jika Anda memilikinya, Anda bisa menambahkan pemeriksaan hasOwnProperty, atau jika Anda tidak keberatan dengan array sementara, gunakan of Object.keys(entry).
Oke, jadi: Tipe. Saya mengira ini akan cukup sulit, tetapi ternyata tidak, yang membuat saya curiga saya melewatkan sesuatu. :-) Kita akan menggunakan fungsi untuk ini sehingga kita bisa menggunakan parameter tipe:
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);
Tautan taman bermain
Karena hasil dibangun seiring waktu, saya menggunakan tipe yang cukup longgar dalam fungsi condense, tetapi input dan output diberi tipe dengan benar. Misalnya, hasil dari result di atas adalah { width: number[]; height: number[]; }, yang memang kita inginkan. Ini bahkan bekerja jika kita menambahkan properti ketiga (misalnya, description) dengan tipe berbeda (misalnya, string), sehingga menghasilkan { width: number[]; height: number[]; description: string[]; }: tautan Playground
Mode saat ini adalah menggunakan reduce, tetapi tidak ada alasan untuk menggunakannya di sini, semua yang dilakukannya hanyalah menambah kompleksitas.
Jawaban #2
Saya menggunakan for loop, karena lebih responsif dan lebih cepat daripada map atau 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)