为JSON对象编写外观模式的最佳实践及代码重构咨询
我正在处理一个JSON文件,主要针对其中的data属性进行数据转换。希望无需每次深入JSON结构,就能便捷地对restaurantName和joined执行.filter等操作。
示例JSON结构:
const data = [ { position: 1, title: "Queues Near You", data: [ [ { restaurantName: "Tonkotsu", joined: false, }, { restaurantName: "BurgerVille", joined: false, }, ], ], }, { position: 2, title: "Restaurants Near You", data: [ [ { restaurantName: "Seoreni", joined: false, }, { restaurantName: "Jinmu", joined: false, }, ], ], }, ];
我编写了外观函数toRestaurantArray,它提取restaurantName并返回二维数组(如[["Tonkotsu","BurgerVille"], ["Seoreni", "Jinmu"]])以简化操作,同时编写了fromRestaurantArray用于从新数组还原JSON结构。
请问:
- 使用
to和from函数的思路是否正确?若不正确,更好的方案是什么? - 若思路正确,该如何重构下方代码的逻辑(忽略
any类型)?我知道fromRestaurant中重复循环JSON结构的代码很差,但不知道优化方法。
当前TypeScript代码:
type RestaurantDetails = { restaurantName: string; joined: boolean; }; type Restaurant = { position: number; title: string; data: Array<Array<RestaurantDetails>>; }; function* zip(arrayOne: any[], arrayTwo: any[]) { const len = arrayOne.length; for (let i = 0; i < len; i++) { yield [arrayOne[i], arrayTwo[i]]; } } // Expected output is to return just the list inside data const toRestaurantsArray = (initialRestaurantsData: Restaurant[]) => { const allRestaurantNames: any = []; for (const eachRestaurantData of initialRestaurantsData) { const [restaurants] = eachRestaurantData.data; const restaurantNames = restaurants.map( ({ restaurantName }) => restaurantName ); allRestaurantNames.push(restaurantNames); } return allRestaurantNames; }; const fromRestaurantsArray = ( restaurantsArray: any[], initialRestaurantsData: any[] ) => { const reconstructedRestaurantsData: any = []; for (const [restaurantNestedArray, eachRestaurantData] of zip( restaurantsArray, initialRestaurantsData )) { const dataProperty: any = []; for (const [restaurantName, eachRestaurantsInnerData] of zip( restaurantNestedArray, eachRestaurantData.data[0] )) { dataProperty.push({ ...eachRestaurantsInnerData, restaurantName: restaurantName, }); } const myTotal = { ...eachRestaurantData, data: dataProperty }; reconstructedRestaurantsData.push(myTotal); } return reconstructedRestaurantsData; }; const restoArray = toRestaurantsArray(data); const filteredArray = restoArray.filter((arr:string[]) => arr.includes("Tonkotsu")) const reconstructedArray = fromRestaurantsArray(filteredArray, data); console.log(reconstructedArray)
一、思路正确性判断
你用to和from函数做数据转换的思路是合理的,核心是把嵌套结构扁平化,降低操作复杂度。但当前实现有个明显局限:toRestaurantsArray只提取了restaurantName,如果之后要操作joined或其他字段,这个函数就无法复用。而且还原时过度依赖原始数据,一旦原始数据变动,还原结果可能出错。
如果你的需求仅针对restaurantName做过滤,当前思路没问题;但如果要支持多字段操作,更灵活的方案是提取完整的RestaurantDetails数组,而非只取名字。这样可以直接对数组里的对象执行filter、map等操作,还原时也不需要重复遍历原始嵌套结构,逻辑更清晰。
二、代码重构方案
1. 优化to函数:提取完整对象数组
把toRestaurantsArray改成提取每层的RestaurantDetails数组,直接支持全字段操作:
const toRestaurantsArray = (initialData: Restaurant[]): RestaurantDetails[][] => { return initialData.map(item => item.data[0]); };
返回结构为[[{restaurantName: "...", joined: ...}, ...], [...]],可直接对每个子数组执行操作,比如过滤包含Tonkotsu的分组:
const restoArray = toRestaurantsArray(data); const filteredArray = restoArray.filter(group => group.some(resto => resto.restaurantName === "Tonkotsu") );
2. 优化from函数:避免重复循环
重构后的from函数利用数组索引对应原始数据位置,直接替换data字段,彻底避免嵌套循环:
const fromRestaurantsArray = (transformedData: RestaurantDetails[][], initialData: Restaurant[]): Restaurant[] => { return transformedData.map((group, index) => ({ ...initialData[index], data: [group] // 还原原始双层数组结构 })); };
3. 完整重构代码
type RestaurantDetails = { restaurantName: string; joined: boolean; }; type Restaurant = { position: number; title: string; data: Array<Array<RestaurantDetails>>; }; // 转换为可直接操作的对象数组 const toRestaurantsArray = (initialData: Restaurant[]): RestaurantDetails[][] => { return initialData.map(item => item.data[0]); }; // 从转换后的数组还原原始结构 const fromRestaurantsArray = (transformedData: RestaurantDetails[][], initialData: Restaurant[]): Restaurant[] => { return transformedData.map((group, idx) => ({ ...initialData[idx], data: [group] })); }; // 使用示例 const restoArray = toRestaurantsArray(data); // 过滤包含Tonkotsu的分组 const filteredArray = restoArray.filter(group => group.some(r => r.restaurantName === "Tonkotsu") ); // 还原结构 const reconstructedArray = fromRestaurantsArray(filteredArray, data); console.log(reconstructedArray);
4. 进一步优化:支持自定义转换
如果需要针对不同字段做转换,可以给to函数添加映射参数,提升扩展性:
const toTransformedArray = <T>( initialData: Restaurant[], mapper: (item: RestaurantDetails) => T ): T[][] => { return initialData.map(item => item.data[0].map(mapper)); }; // 提取restaurantName const nameArray = toTransformedArray(data, r => r.restaurantName); // 提取joined状态 const joinedArray = toTransformedArray(data, r => r.joined);
内容的提问来源于stack exchange,提问作者meg hidey

