如何使用JS的Reduce函数对数组中同名对象分组?
如何用reduce合并同名的文档列表项?
我有一个对象数组,目前通过filter和map进行筛选与渲染,代码如下:
const countries = [ "de", "fr", "nl" ]; const renderDocItem = (doc, country) => { return ( <li className="tabsLi" key={doc._id}> <div className="d-flex w-75 justify-content-between"> <div> {doc.name} <br /> <span className="filename">{doc.filename}</span> </div> <div> {doc.master ? ( <img className="flag" src={en} /> ) : ( <img className="flag" src={objectOfCountries[country]} /> )} <span className="action"> [ <a href="#" onClick={() => view(doc, "view")}> view </a> ,{" "} <a href="#" onClick={() => view(doc, "edit")}> edit </a> ] </span> </div> </div> </li> ); }; const renderDocsPerCountry = (docs, country, category) => { const filteredDocs = docs.filter((doc) => { return doc.countries.includes(country) && doc.category == category; }); const renderedDocs = filteredDocs.map((doc) => { return renderDocItem(doc, country); }); console.log(renderedDocs); return renderedDocs; }; <ul>{renderDocsPerCountry(docs, country, "xxx")}</ul>
这段代码会生成按“xxx”分类、按国家分组的列表项。问题在于部分对象拥有相同的name属性但所属国家不同,我希望将同名的对象进行分组合并。
当前输出:
<ul> <li>Name1, filename1, country1, link</li> <li>Name1, filename2, country2, link</li> <li>Name3, filename3, country3, link</li> </ul>
期望输出:
<ul> <li>Name1, filename1/filename2, country 1 - link / country 2 - link</li> <li>Name3, filename3, country3, link</li> </ul>
请问对应的reduce函数应该如何编写?
解决方案
你可以通过reduce先按name对过滤后的文档进行分组,把同一名称下的文档信息整合,再渲染合并后的列表项:
- 修改
renderDocsPerCountry函数,整合筛选、分组与渲染逻辑:
const renderDocsPerCountry = (docs, country, category) => { // 筛选符合分类与国家条件的文档 const filteredDocs = docs.filter(doc => doc.countries.includes(country) && doc.category === category ); // 用reduce按name分组,聚合文件名和文档数据 const groupedDocs = filteredDocs.reduce((acc, doc) => { const existingGroup = acc.find(group => group.name === doc.name); if (existingGroup) { // 已有同名分组,追加数据 existingGroup.filenames.push(doc.filename); existingGroup.items.push(doc); } else { // 无同名分组,创建新分组 acc.push({ name: doc.name, filenames: [doc.filename], items: [doc] }); } return acc; }, []); // 渲染合并后的列表项 return groupedDocs.map(group => ( <li className="tabsLi" key={group.name}> <div className="d-flex w-75 justify-content-between"> <div> {group.name} <br /> <span className="filename">{group.filenames.join('/')}</span> </div> <div> {/* 遍历分组内的所有文档,渲染对应的标识和操作链接 */} {group.items.map((item, idx) => ( <span key={idx} style={{ marginRight: '12px' }}> {item.master ? ( <img className="flag" src={en} /> ) : ( <img className="flag" src={objectOfCountries[country]} /> )} <span className="action"> [ <a href="#" onClick={() => view(item, "view")}>view</a> ,{" "} <a href="#" onClick={() => view(item, "edit")}>edit</a> ] </span> {/* 非最后一项添加分隔符 */} {idx < group.items.length - 1 && ' / '} </span> ))} </div> </div> </li> )); };
- 核心逻辑说明:
reduce的作用是将相同name的文档聚合为一个分组对象,保存名称、所有关联文件名,以及完整的文档数据(用于后续渲染操作链接和国家标识)- 渲染阶段,将同一分组的文件名用
/拼接,同时遍历分组内的每个文档,渲染对应的国家标识和操作链接,并用/分隔不同国家的操作区域
这样就能实现你需要的同名项合并效果。
内容的提问来源于stack exchange,提问作者Eric Mitjans
相关产品推荐
相关产品推荐

