Вот решение, которое немного обобщенно c, позволяющее вам указать, что вы пытаетесь сопоставить и во что. Он использует объект Map
, чтобы помочь с сопоставлением целевого ключа (user_id
в вашем случае):
function collate(sourceArray, keyName, collectionName, collectionFields) {
let collection = new Map();
for (let item of sourceArray) {
let targetObj = collection.get(item[keyName]);
if (!targetObj) {
targetObj = Object.assign({}, item);
// remove the properties we are collecting
for (let field of collectionFields) {
delete targetObj[field];
}
targetObj[collectionName] = [];
collection.set(item[keyName], targetObj);
}
let collectedObj = {};
// copy over the fields we are collecting
for (let field of collectionFields) {
collectedObj[field] = item[field];
}
targetObj[collectionName].push(collectedObj);
}
// now convert Map to the final array
return Array.from(collection.values());
}
Потому что я думаю, что это имеет гораздо больше смысла для тех, кто использует Получая данные, он всегда помещает поля адреса в массив, даже если существует только один адрес и, следовательно, его массив длиной 1
. Это значительно облегчит использование итерации этих данных без всевозможных особых условий для их чтения, поскольку данные последовательно расположены независимо от того, существует ли один или несколько адресов. Читатель всегда может определить длину массива, чтобы увидеть, сколько там адресов.
И вы можете использовать его в своих данных следующим образом:
let data = [
{
"user_id": 1,
"name": "test name",
"address_id": 1,
"address": "1234 address way"
},
{
"user_id": 1,
"name": "test name",
"address_id": 2,
"address": "5678 new address"
},
{
"user_id": 2,
"name": "diff name",
"address_id": 1,
"address": "1234 address way"
}
];
function collate(sourceArray, keyName, collectionName, collectionFields) {
let collection = new Map();
for (let item of sourceArray) {
let targetObj = collection.get(item[keyName]);
if (!targetObj) {
targetObj = Object.assign({}, item);
// remove the properties we are collecting
for (let field of collectionFields) {
delete targetObj[field];
}
targetObj[collectionName] = [];
collection.set(item[keyName], targetObj);
}
let collectedObj = {};
// copy over the fields we are collecting
for (let field of collectionFields) {
collectedObj[field] = item[field];
}
targetObj[collectionName].push(collectedObj);
}
// now convert Map to the final array
return Array.from(collection.values());
}
let result = collate(data, "user_id", "address", ["address", "address_id"]);
console.log(result);
Или, если вы действительно не хотите массив из одного элемента, вы можете опубликовать этот процесс в конце:
let data = [
{
"user_id": 1,
"name": "test name",
"address_id": 1,
"address": "1234 address way"
},
{
"user_id": 1,
"name": "test name",
"address_id": 2,
"address": "5678 new address"
},
{
"user_id": 2,
"name": "diff name",
"address_id": 1,
"address": "1234 address way"
}
];
function collate(sourceArray, keyName, collectionName, collectionFields) {
let collection = new Map();
for (let item of sourceArray) {
let targetObj = collection.get(item[keyName]);
if (!targetObj) {
targetObj = Object.assign({}, item);
// remove the properties we are collecting
for (let field of collectionFields) {
delete targetObj[field];
}
targetObj[collectionName] = [];
collection.set(item[keyName], targetObj);
}
let collectedObj = {};
// copy over the fields we are collecting
for (let field of collectionFields) {
collectedObj[field] = item[field];
}
targetObj[collectionName].push(collectedObj);
}
// now convert Map to the final array
let result = Array.from(collection.values());
// remove single element arrays and copy back to main object
for (let item of result) {
let array = item[collectionName];
if (array.length === 1) {
// remove the array
delete item[collectionName];
// copy the fields from the one array element back to the main object
Object.assign(item, array[0]);
}
}
return result;
}
let result = collate(data, "user_id", "address", ["address", "address_id"]);
console.log(result);