
本文介绍如何在 Node.js 中对二维数组按第一列值(如 @test)分组,并对每个分组剔除首个元素,仅保留后续项,适用于版本号批量处理等场景。
本文介绍如何在 node.js 中对二维数组按第一列值(如 `@test`)分组,并对每个分组剔除首个元素,仅保留后续项,适用于版本号批量处理等场景。
在 Node.js 开发中,常需对结构化数据(如 [name, version] 形式的二维数组)进行分组与筛选操作。例如,你有一组带 -unstable 后缀的包版本数据,希望按包名(@test、@test2 等)分组后,忽略每个分组中的第一个版本(通常为初始/基础版本),仅对后续版本执行后续操作(如调用 API 更新或发布)。
以下提供两种兼容性不同的实现方案:
✅ 方案一:现代写法(推荐,Node.js ≥ 18.0+,支持 Object.groupBy)
const data = [
['@test', '1.2.6-unstable'],
['@test', '1.3.2-unstable'],
['@test', '1.4.6-unstable'],
['@test2', '4.0.1-unstable'],
['@test2', '4.0.2-unstable'],
['@test2', '4.0.3-unstable'],
['@test3', '2.2.0-unstable'],
['@test3', '2.2.3-unstable'],
['@test3', '2.2.9-unstable']
];
// 步骤 1:按首元素(包名)分组 → 得到 { '@test': [...], '@test2': [...], ... }
// 步骤 2:提取所有分组值(Object.values)
// 步骤 3:对每个分组调用 .slice(1) 剔除首个项
const groupedAndTrimmed = Object.values(
Object.groupBy(data, ([name]) => name)
).map(group => group.slice(1));
console.log(groupedAndTrimmed);
// 输出:
// [
// [ ['@test','1.3.2-unstable'], ['@test','1.4.6-unstable'] ],
// [ ['@test2','4.0.2-unstable'], ['@test2','4.0.3-unstable'] ],
// [ ['@test3','2.2.3-unstable'], ['@test3','2.2.9-unstable'] ]
// ]✅ 优势:语义清晰、链式简洁、原生支持、无需额外依赖。
⚠️ 注意:Object.groupBy是 ECMAScript 2024 新特性,在 Node.js 18.0+(V8 10.2+)中默认启用;若使用旧版 Node.js,请降级使用方案二。
✅ 方案二:通用写法(兼容 Node.js ≥ 12,无新 API 依赖)
const groupedAndTrimmed = Object.values(
data.reduce((acc, item) => {
const key = item[0];
if (!acc[key]) acc[key] = [];
acc[key].push(item);
return acc;
}, {})
).map(group => group.slice(1));该写法手动模拟分组逻辑,使用普通对象作为哈希表,完全兼容主流 Node.js 版本(包括 LTS 版本如 16.x / 18.x / 20.x),且性能稳定。
ZenHeart zenlink 与 zenlink-mcp 为 OpenClaw,涵盖安装、MCP 注册、钩子接线、入站轮询及升级维护。
? 后续操作示例:为每个剩余版本发起 HTTP 请求
结合你的实际需求(如调用 Azure DevOps REST API),可在分组处理后遍历执行:
const axios = require('axios');
// 假设 groupedAndTrimmed 已生成
for (const group of groupedAndTrimmed) {
for (const [name, version] of group) {
const url = `https://dev.azure.com/your-org/_apis/packaging/feeds/your-feed/npm/packages/${encodeURIComponent(name)}/versions/${encodeURIComponent(version)}?api-version=7.1-preview.1`;
try {
await axios.delete(url, { headers: { 'Authorization': 'Bearer YOUR_TOKEN' } });
console.log(`✅ Deleted ${name}@${version}`);
} catch (err) {
console.error(`❌ Failed to delete ${name}@${version}:`, err.response?.statusText || err.message);
}
}
}? 关键注意事项
-
slice(1)返回新数组,不修改原始数据,符合函数式编程习惯; - 分组键(如
@test)必须是字符串类型;若存在undefined或null,需提前校验; - 若原始数据未按包名有序排列,
slice(1)仍正确生效——它始终剔除该分组内第一个插入的项,而非全局序号; - 实际生产中建议添加防错逻辑:检查分组长度
if (group.length > 1)再执行.slice(1),避免空数组误操作。
掌握这种“分组 → 截断 → 批量处理”模式,可高效支撑 CI/CD 流水线中的版本清理、灰度发布、依赖分析等典型任务。

















