I am trying to merge related objects in an array of objects based on similar properties in a specific object key "name".
我正在尝试根据特定对象键“name”中的相似属性合并对象数组中的相关对象。
I have tried the following code but did not work:
我尝试了以下代码,但不起作用:
const students = [];
const obj = [{
"name": "A. Nasila",
"adm": "",
"class": "4p",
"mat": "80",
"eng": "",
"kis": ""
},
{
"name": "A. Nasila",
"adm": "4886",
"class": "",
"mat": "",
"eng": "",
"kis": "39"
},
{
"name": "Mtu Bure",
"adm": "",
"class": "3L",
"mat": "",
"eng": "86",
"kis": ""
},
{
"name": "A. Nasila",
"adm": "",
"class": "",
"mat": "",
"eng": "75",
"kis": ""
},
{
"name": "Mtu Bure",
"adm": "9790",
"class": "",
"mat": "77",
"eng": "",
"kis": "76"
}
];
const groupedData = obj.reduce((acc, cur) => {
const name = cur.name;
if (!acc[name]) {
acc[name] = {};
}
for (const key in cur) {
acc[name][key] = cur[key];
}
return acc;
}, {});
const result = Object.values(groupedData);
const jsonObject = JSON.stringify(result);
const div = document.getElementById("students");
const span = document.createElement("span");
span.innerHTML = jsonObject;
div.appendChild(span);
<div id="students">
</div>
Help me get the desired output as shown below;
帮助我获得所需的输出,如下所示;
[
{
"name": "A. Nasila",
"adm": "4886",
"class": "4p",
"mat": "80",
"eng": "75",
"kis": "39"
},
{
"name": "Mtu Bure",
"adm": "9790",
"class": "3L",
"mat": "77",
"eng": "86",
"kis": "76"
}
]
更多回答
You are overwriting every key on each iteration even if it is an empty string, so it will simply keep the last object by name. You need to check if the value is empty before setting it. for (const key in cur) { if (acc[name][key] === undefined || acc[name][key] === "") acc[name][key] = cur[key]; }
您将覆盖每个迭代中的每个键,即使它是一个空字符串,因此它只会按名称保留最后一个对象。在设置之前,您需要检查该值是否为空。For(Cur中的常量键){if(acc[名称][键]=未定义||acc[名称][键]=“”)acc[名称][键]=cur[键];}
you can also use ??= operator ( Nullish coalescing assignment )
您还可以使用??=运算符(Null合并赋值)
const obj =
[ { name: 'A. Nasila', adm: '', class: '4p', mat: '80', eng: '', kis: '' }
, { name: 'A. Nasila', adm: '4886', class: '', mat: '', eng: '', kis: '39' }
, { name: 'Mtu Bure', adm: '', class: '3L', mat: '', eng: '86', kis: '' }
, { name: 'A. Nasila', adm: '', class: '', mat: '', eng: '75', kis: '' }
, { name: 'Mtu Bure', adm: '9790', class: '', mat: '77', eng: '', kis: '76' }
];
const res = Object.values(obj.reduce((r,o) =>
{
r[o.name] ??= {};
Object.keys(o).forEach(k => r[o.name][k] ||= o[k]);
return r;
}, {}));
console.log( res )
.as-console-wrapper {max-height: 100% !important;top: 0;}
.as-console-row::after {display: none !important;}
This should work
这应该行得通
const students = [];
const obj = [
{
"name": "A. Nasila",
"adm": "",
"class": "4p",
"mat": "80",
"eng": "",
"kis": ""
},
{
"name": "A. Nasila",
"adm": "4886",
"class": "",
"mat": "",
"eng": "",
"kis": "39"
},
{
"name": "Mtu Bure",
"adm": "",
"class": "3L",
"mat": "",
"eng": "86",
"kis": ""
},
{
"name": "A. Nasila",
"adm": "",
"class": "",
"mat": "",
"eng": "75",
"kis": ""
},
{
"name": "Mtu Bure",
"adm": "9790",
"class": "",
"mat": "77",
"eng": "",
"kis": "76"
}
];
const groupedData = obj.reduce((acc, cur) => {
const name = cur.name;
if (!acc[name]) {
acc[name] = {};
}
// Iterate through the keys of the current object and merge them
for (const key in cur) {
if (cur.hasOwnProperty(key)) {
acc[name][key] = cur[key] || acc[name][key];
}
}
return acc;
}, {});
const result = Object.values(groupedData);
const jsonObject = JSON.stringify(result);
const div = document.getElementById("students");
const span = document.createElement("span");
span.innerHTML = jsonObject;
div.appendChild(span);
<!DOCTYPE html>
<html>
<head>
<title>Students</title>
</head>
<body>
<div id="students">
</div>
</body>
</html>
A little explanation of what's going on
对正在发生的事情作一点解释
First off, I used the reduce method to iterate through the array of objects (obj) and group them by the name
property into the groupedData
object.
首先,我使用Reduced方法迭代对象数组(Obj),并按名称属性将它们分组到grouedData对象中。
If the name
property is encountered for the first time, I then create a new object for it. If it has already been encountered, we merge the current object's properties into the existing object.
如果第一次遇到name属性,我就会为它创建一个新对象。如果已经遇到它,则将当前对象的属性合并到现有对象中。
I then converted the groupedData
object into an array of values using Object.values
method.
然后,我使用Object.Values方法将grouedData对象转换为一个值数组。
EDIT: Made it work dynamically with all keys.
编辑:使其与所有关键点动态工作。
Avoid using spread, intermediate arrays, and getting full object data with Object.entries()
for best performance.
为了获得最佳性能,请避免使用散布、中间数组,并使用对象条目()获取完整的对象数据。
It's also not clear what to do if 2 objects contain the same non-empty property, while I overwrite with the latest, Mister Jojo for example keeps the first one.
如果两个对象包含相同的非空属性,而我用最新的对象覆盖,例如,Mister Jojo保留第一个对象,那么应该怎么做也不清楚。
console.log(
obj.reduce((r, item, obj) => (
(obj = r.map.get(item.name)) ?
Object.keys(item).forEach(k => k !== 'name' && item[k] && (obj[k] = item[k])) :
r.map.set(item.name, r.arr[r.arr.length] = item) , r
), {arr: [], map: new Map}
).arr
);
<script>
const obj = [{
"name": "A. Nasila",
"adm": "",
"class": "4p",
"mat": "80",
"eng": "",
"kis": ""
},
{
"name": "A. Nasila",
"adm": "4886",
"class": "",
"mat": "",
"eng": "",
"kis": "39"
},
{
"name": "Mtu Bure",
"adm": "",
"class": "3L",
"mat": "",
"eng": "86",
"kis": ""
},
{
"name": "A. Nasila",
"adm": "",
"class": "",
"mat": "",
"eng": "75",
"kis": ""
},
{
"name": "Mtu Bure",
"adm": "9790",
"class": "",
"mat": "77",
"eng": "",
"kis": "76"
}
];
</script>
And a benchmark:
和一个基准:
<script benchmark data-count="1000000">
const obj = [{
"name": "A. Nasila",
"adm": "",
"class": "4p",
"mat": "80",
"eng": "",
"kis": ""
},
{
"name": "A. Nasila",
"adm": "4886",
"class": "",
"mat": "",
"eng": "",
"kis": "39"
},
{
"name": "Mtu Bure",
"adm": "",
"class": "3L",
"mat": "",
"eng": "86",
"kis": ""
},
{
"name": "A. Nasila",
"adm": "",
"class": "",
"mat": "",
"eng": "75",
"kis": ""
},
{
"name": "Mtu Bure",
"adm": "9790",
"class": "",
"mat": "77",
"eng": "",
"kis": "76"
}
];
// @benchmark Jojo
Object.values(obj.reduce((r,o) =>
{
r[o.name] ??= {};
Object.keys(o).forEach(k => r[o.name][k] ||= o[k]);
return r;
},{}));
// @benchmark Alexander
obj.reduce((r, item, obj) => {
(obj = r.map.get(item.name)) ?
Object.keys(item).forEach(k => k !== 'name' && item[k] && (obj[k] = item[k])) :
r.map.set(item.name, r.arr[r.arr.length] = item);
return r;
}, {arr: [], map: new Map}
).arr
</script>
<script src="https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js"></script>
Is there a way to iterate through the code you've written inside if & else. Because am supposing it will be tiresome to use it on an object with many keys.
- If you can assume they'll always be the same, you can hardcode them in a list.
- If you can assume they'll always be the same in the same array, but can't be hardcoded, use
Object.keys()
on the first object.
Then you can construct an object from an array of [key, value]
tuples thanks to Object.fromEntries()
.
然后,您就可以使用由[key,Value]元组组成的数组来构造对象,这要归功于Object.FromEntry()。
You can't use the ??
operator for your values because you are using falsy values like empty strings.
你不能使用??运算符,因为您使用的是空字符串之类的假值。
const keys = Object.keys(obj[0])
const result = Object.values(
obj.reduce(
(acc, curr) => ({
...acc,
[curr.name]: Object.fromEntries(
keys.map((key) => [key, curr[key] || acc[curr.name]?.[key]])
),
}),
{}
)
)
PS: In terms of performance, some other answers are a lot better than this one. But unless this code will be executed in a hot path of your application, there is no need to optimize this, and make your code unreadable.
PS:就性能而言,其他一些答案比这个好得多。但是,除非该代码将在应用程序的热路径中执行,否则没有必要对此进行优化,从而使您的代码不可读。
更多回答
@Drarig29 That's my answer, so I'll use my indentation style.
@drarig29这就是我的答案,所以我将使用我的缩进风格。
@MisterJojo Fine 😉
@MisterJojo Fine😉
That is great! Is there a way to iterate through the code you've written inside if & else. Because am supposing it will be tiresome to use it on an object with many keys.
太棒了!有没有一种方法可以迭代您在If&Else中编写的代码。因为我认为在一个有许多关键点的对象上使用它会很麻烦。
@AbednegoNasila I have updated my answer to match your requirement.
@AbednegoNasila我已经更新了我的答案以满足您的要求。
If the obj
array is very long, you might want to omit name
from keys
.
如果obj数组很长,您可能希望从键中省略name。
Well, if the OP really needs something optimized because it's a hot path of their application, they should not use my answer at all.
好吧,如果OP确实需要优化一些东西,因为这是他们的应用程序的热门路径,他们根本不应该使用我的答案。
我是一名优秀的程序员,十分优秀!