如何优雅地遍历Toggl API返回的数组并按用户名分组提取指定数据
如何优雅地遍历Toggl API返回的数组并按用户名分组提取指定数据
这是个很典型的分组合并需求,你想用伪代码实现的思路没问题,但其实用数组的reduce方法+映射表的方式会更优雅高效,代码也更简洁,我给你详细讲讲怎么实现:
首先先确认下你提供的原始Toggl API返回数据(我修正了语法错误,确保是合法的JSON数组):
[ { "user_id": 12345, "username": "JohnSmith", "description": "foo", "time_entries": [ { "id": 3778920070, "seconds": 10800, "start": "2025-01-27T11:00:00+00:00", "stop": "2025-01-27T14:00:00+00:00", "at": "2025-01-27T16:17:24+00:00", "at_tz": "2025-01-27T16:17:24+00:00" } ], "row_number": 1 }, { "user_id": 6789, "username": "JaneSmith", "description": "bar", "time_entries": [ { "id": 3778684944, "seconds": 5977, "start": "2025-01-27T14:35:31+00:00", "stop": "2025-01-27T16:15:08+00:00", "at": "2025-01-27T16:15:09+00:00", "at_tz": "2025-01-27T16:15:09+00:00" } ], "row_number": 2 }, { "user_id": 12345, "username": "JohnSmith", "description": "bar", "time_entries": [ { "id": 3780521038, "seconds": 3600, "start": "2025-01-27T09:00:00+00:00", "stop": "2025-01-27T10:00:00+00:00", "at": "2025-01-28T13:34:31+00:00", "at_tz": "2025-01-28T13:34:31+00:00" } ], "row_number": 3 } ]
优雅的分组实现方案
我们可以用reduce方法来遍历数组,同时维护一个以用户名为键的临时对象(映射表),这样就能在O(n)的时间复杂度内完成分组(比嵌套循环的O(n²)高效太多,数据量大的时候差异特别明显):
// 假设原始数据是上面的togglData数组 const groupedData = Object.values(togglData.reduce((acc, currentItem) => { // 用用户名作为唯一标识键 const usernameKey = currentItem.username; if (!acc[usernameKey]) { // 第一次遇到该用户:复制所有字段,直接存入映射表 acc[usernameKey] = { ...currentItem }; } else { // 用户已存在:合并time_entries数组 acc[usernameKey].time_entries = [ ...acc[usernameKey].time_entries, ...currentItem.time_entries ]; // 额外说明:如果需要处理其他字段(比如description/row_number),可以在这里加逻辑 // 例如要保留最后一次的description:acc[usernameKey].description = currentItem.description; // 或者合并成数组:acc[usernameKey].descriptions = [...(acc[usernameKey].descriptions || []), currentItem.description]; } return acc; }, {})); // 输出结果 console.log(JSON.stringify(groupedData, null, 2));
代码逻辑解释
- reduce方法:它的核心是遍历数组并累积一个结果,这里我们用它来构建用户分组的映射表。
- 映射表acc:键是用户名,值是分组后的用户数据,这样查找用户是否存在的操作是O(1)的,非常高效。
- 扩展运算符
...:用来浅拷贝对象和数组,避免修改原始数据,保证数据的纯净性。 - Object.values():最后把映射表的所有值提取出来,转换成你需要的数组格式。
最终输出效果
运行上面的代码后,你会得到按用户名分组、合并了time_entries的结果,和你期望的输出一致:
[ { "user_id": 12345, "username": "JohnSmith", "description": "foo", "time_entries": [ { "id": 3778920070, "seconds": 10800, "start": "2025-01-27T11:00:00+00:00", "stop": "2025-01-27T14:00:00+00:00", "at": "2025-01-27T16:17:24+00:00", "at_tz": "2025-01-27T16:17:24+00:00" }, { "id": 3780521038, "seconds": 3600, "start": "2025-01-27T09:00:00+00:00", "stop": "2025-01-27T10:00:00+00:00", "at": "2025-01-28T13:34:31+00:00", "at_tz": "2025-01-28T13:34:31+00:00" } ], "row_number": 1 }, { "user_id": 6789, "username": "JaneSmith", "description": "bar", "time_entries": [ { "id": 3778684944, "seconds": 5977, "start": "2025-01-27T14:35:31+00:00", "stop": "2025-01-27T16:15:08+00:00", "at": "2025-01-27T16:15:09+00:00", "at_tz": "2025-01-27T16:15:09+00:00" } ], "row_number": 2 } ]
这种方式既简洁又高效,完全符合你想要的优雅需求,而且扩展性很强,后续如果需要处理其他字段的合并逻辑,只需要在else块里添加对应的代码即可。
备注:内容来源于stack exchange,提问作者Gary
相关产品推荐
相关产品推荐

