使用现代JavaScript查找深度嵌套对象中的指定产品
如何用现代JS方法从嵌套数据中查找目标PurchaseCardRecord对象
由于DatoCMS的GraphQL Schema不支持where过滤器,只能查询全部数据后在本地按ID查找目标对象。目标PurchaseCardRecord对象嵌套在foo.data.allGiveawayLandingPages→lpSection→products数组内,尝试多种ES6方法均未成功,仅旧版递归有效,现寻求更优雅的现代解法。
数据结构示例
"foo": { "data": { "allGiveawayLandingPages": [ { "lpSection": [ {}, {}, {}, {}, {}, {}, {}, { "id": "34525949", "products": [ { "__typename": "PurchaseCardRecord", "discountAmount": 50, "discountAmountPct": null, "discountEndDate": "2022-11-01T23:00:00+00:00", "id": "44144096" }, { "__typename": "PurchaseCardRecord", "discountAmount": null, "discountAmountPct": null, "discountEndDate": null, "id": "44144097" } ] } ] } ] } }
尝试过的失败方法
1. 嵌套find方法
const foo = await request({ query: ALL_PURCHASE_CARDS_QUERY }); const card = foo.data.allGiveawayLandingPages.find((page) => { return page.lpSection.find((section) => { return section?.products.find((record) => record.id === parentId) }) }); logger.debug({card}, 'Got card');
问题:find返回的是第一个满足条件的父级对象(allGiveawayLandingPages中的页面),而非目标PurchaseCardRecord本身;遇到空section时,section?.products会返回undefined,可能导致逻辑中断,且报错时错误对象为空无法调试。
2. reduce方法
const card = foo.data.allGiveawayLandingPages.reduce((accumulator, item) => { return item?.lpSection.reduce((section) => { return section?.products.reduce((record) => { if ('PurchaseCardRecord' === record.__typename && record.id === parentId) return record; }) }) })
问题:reduce用法错误,未正确处理累加器参数,每一层reduce都没有返回有效的累加值,最终无法得到目标对象,反而可能返回父对象或undefined。
当前有效的旧版递归方法
function filterCards(object) { if (object.hasOwnProperty('__typename') && object.hasOwnProperty('id') && ('PurchaseCardRecord' === object.__typename && parentId === object.id)) return object; for (let i=0; i<Object.keys(object).length; i++) { if (typeof object[Object.keys(object)[i]] == 'object') { const o = filterCards(object[Object.keys(object)[i]]); if (o != null) return o; } } return null; }
该方法能正常工作,但写法偏老旧,不够简洁。
优化后的现代JS解法
方法一:扁平化嵌套数组后直接查找
利用flatMap将嵌套数组展开为一维数组,再用find定位目标,逻辑清晰简洁:
const foo = await request({ query: ALL_PURCHASE_CARDS_QUERY }); const targetCard = foo.data.allGiveawayLandingPages .flatMap(page => page.lpSection) .flatMap(section => section?.products || []) .find(record => record?.__typename === 'PurchaseCardRecord' && record?.id === parentId);
说明:flatMap依次展开lpSection和products数组,section?.products || []处理空section避免报错,最后直接查找匹配ID和类型的对象。
方法二:现代递归写法
保留递归的灵活性,用现代JS语法简化代码:
const findTargetCard = (obj) => { if (obj?.__typename === 'PurchaseCardRecord' && obj?.id === parentId) return obj; for (const value of Object.values(obj)) { if (typeof value === 'object' && value !== null) { const result = findTargetCard(value); if (result) return result; } } return null; }; const foo = await request({ query: ALL_PURCHASE_CARDS_QUERY }); const targetCard = findTargetCard(foo.data.allGiveawayLandingPages);
说明:用Object.values替代Object.keys遍历属性值,更直观;用可选链?.简化属性判断,代码更简洁易读。
内容的提问来源于stack exchange,提问作者philolegein
相关产品推荐
相关产品推荐

