
本文介绍如何将一个以数字为键的对象,按其子对象的某个字符串属性(如 name)进行分组统计,最终生成包含唯一值及其出现次数的新对象。
本文介绍如何将一个以数字为键的对象,按其子对象的某个字符串属性(如 name)进行分组统计,最终生成包含唯一值及其出现次数的新对象。
在 JavaScript 中,原始数据常以“类数组对象”形式存在(即键为连续数字字符串,但并非真正的 Array),例如:
const input = {
0: { State: true, Name: 'bob' },
1: { State: true, Name: 'candice' },
2: { State: true, Name: 'allen' },
3: { State: true, Name: 'bob' },
4: { State: true, Name: 'bob' },
5: { State: true, Name: 'candice' }
};
目标是将其转换为按 Name 去重并计数的结果对象,键为递增数字索引,值为 { Name, Count } 形式:
{
0: { Name: 'bob', Count: 3 },
1: { Name: 'candice', Count: 2 },
2: { Name: 'allen', Count: 1 }
}
✅ 推荐实现(简洁、可读、兼容性好)
使用 Object.values() 获取所有子对象,通过 Map 或普通对象做频次统计,再转为带序号键的目标结构:
function flattenAndCount(obj, key = 'Name') {
// 步骤1:统计各 name 出现次数
const countMap = {};
for (const item of Object.values(obj)) {
const value = item[key];
countMap[value] = (countMap[value] ?? 0) + 1;
}
// 步骤2:按字典序或原始出现顺序整理(此处保持 Object.keys() 的枚举顺序,实际中建议显式排序)
return Object.entries(countMap).reduce((acc, [name, count], index) => {
acc[index] = { Name: name, Count: count };
return acc;
}, {});
}
// 使用示例
console.log(flattenAndCount(input));
// 输出:
// {
// 0: { Name: 'bob', Count: 3 },
// 1: { Name: 'candice', Count: 2 },
// 2: { Name: 'allen', Count: 1 }
// }
? 关键说明与注意事项
- 不要误用 Array.from() 或 map():原始输入是对象而非数组,直接调用 Array.prototype 方法可能引发错误或隐式类型转换风险。
- ??(空值合并运算符)要求 ES2020+ 环境;若需兼容旧环境,可用 countMap[value] = (countMap[value] || 0) + 1 替代。
- 顺序保证:Object.keys() / Object.entries() 在现代引擎中按插入顺序返回(ECMAScript 2015+ 规范),因此结果顺序与首次出现顺序一致;如需严格按字母排序,可在 Object.entries() 后加 .sort(([a], [b]) => a.localeCompare(b))。
- 扩展性提示:函数支持自定义统计字段(如传入 'Name'、'Category' 等),便于复用。
? 进阶建议(更函数式写法)
若偏好链式风格且项目已引入 Lodash,也可用:
import { countBy, toPairs, fromPairs } from 'lodash';
const result = Object.fromEntries(
toPairs(countBy(Object.values(input), 'Name'))
.map(([Name, Count], i) => [i, { Name, Count }])
);
但原生方案无依赖、性能优、语义清晰,推荐作为默认实践。











