
本文介绍一种使用 map 结构高效处理图书库存变更的方案,解决在操作过程中因嵌套循环和原地修改导致的重复添加、无限循环及逻辑错乱问题,并确保新增图书自动入库、零库存图书自动剔除。
本文介绍一种使用 map 结构高效处理图书库存变更的方案,解决在操作过程中因嵌套循环和原地修改导致的重复添加、无限循环及逻辑错乱问题,并确保新增图书自动入库、零库存图书自动剔除。
在图书馆库存管理场景中,catalogus(初始库存)与 dagverloop(当日借还流水)需实时同步:
- 遇
"uitlening"(借出),对应 ISBN 的aantal减 1; - 遇
"teruggave"(归还),对应 ISBN 的aantal加 1; - 若
aantal降至 0,该图书应从最终库存中移除; - 若流水中的 ISBN 不在原始库存中,则需以
{ ISBN: "...", aantal: 1 }形式自动添加。
传统双层 for 循环+splice/push 的做法极易引发以下问题:
- 边遍历边修改数组长度 →
j索引错位、漏处理或重复处理; -
else if分支逻辑耦合过强,未覆盖“ISBN 不存在且为teruggave”这一关键新增场景; - 多次
push同一 ISBN → 数据冗余; - 无统一状态中心 → 状态判断碎片化。
✅ 推荐解法:用 Map 作为核心状态容器,实现 O(1) 查找、安全更新与清晰聚合。
步骤一:构建 ISBN → aantal 映射表
const inventoryMap = new Map(catalogus.map(book => [book.ISBN, book.aantal]));
此行将原始数组转为键值对结构,天然支持快速存在性判断与数值更新。
步骤二:单次遍历流水,原子化更新
for (const record of dagverloop) {
const currentCount = inventoryMap.get(record.ISBN) ?? 0;
const newCount = record.handeling === 'teruggave' ? currentCount + 1 : currentCount - 1;
inventoryMap.set(record.ISBN, newCount);
}
-
?? 0确保新 ISBN 首次出现时以0为基值,后续+1即得1,自然完成“首次归还即入库”; - 无需手动
push或splice,所有变更集中于 Map,避免副作用。
步骤三:生成最终库存(过滤零值 + 格式化)
const updatedCatalogus = [...inventoryMap.entries()]
.filter(([isbn, count]) => count > 0) // 移除零库存项
.map(([isbn, count]) => ({ ISBN: isbn, aantal: count }));
-
filter在转换前剔除aantal ≤ 0的条目,语义清晰; -
map将[key, value]元组还原为标准对象格式。
✅ 完整可运行示例
let catalogus = [
{ ISBN: "978-1449355739", aantal: 1 },
{ ISBN: "978-0596806750", aantal: 2 },
{ ISBN: "978-0596805524", aantal: 1 },
{ ISBN: "978-1491905012", aantal: 1 },
{ ISBN: "978-0596008642", aantal: 3 },
{ ISBN: "978-0596004897", aantal: 2 }
];
const dagverloop = [
{ ISBN: "978-0596806750", handeling: "uitlening" },
{ ISBN: "978-1491905012", handeling: "teruggave" },
{ ISBN: "978-0596805524", handeling: "uitlening" },
{ ISBN: "978-1449319243", handeling: "teruggave" },
{ ISBN: "978-1491905012", handeling: "uitlening" },
{ ISBN: "978-0596004897", handeling: "uitlening" },
{ ISBN: "978-1491908426", handeling: "teruggave" },
{ ISBN: "978-1449319243", handeling: "uitlening" },
{ ISBN: "978-0596004361", handeling: "teruggave" },
{ ISBN: "978-1491905012", handeling: "uitlening" },
{ ISBN: "978-1449355739", handeling: "uitlening" }
];
// 核心三步法
const inventoryMap = new Map(catalogus.map(b => [b.ISBN, b.aantal]));
for (const r of dagverloop) {
const cur = inventoryMap.get(r.ISBN) ?? 0;
inventoryMap.set(r.ISBN, r.handeling === 'teruggave' ? cur + 1 : cur - 1);
}
const result = [...inventoryMap.entries()]
.filter(([, count]) => count > 0)
.map(([isbn, count]) => ({ ISBN: isbn, aantal: count }));
console.log(result);
// 输出 5 个对象:4 个 `aantal: 1`,1 个 `aantal: 3` —— 符合预期
⚠️ 注意事项
-
勿在循环中直接修改被遍历的数组(如
catalogus.push/splice),这是原始代码陷入死循环或重复添加的根源; -
Map是引用类型,若需保留原始catalogus,请确保后续操作基于新生成的数组(如result); - 若业务要求保留
aantal === 0的记录(仅标记为售罄),可移除.filter()步骤; - 对超大规模数据(>10 万条),可考虑使用
WeakMap或分片处理,但本场景Map已足够高效。
该方案以数据结构驱动逻辑简化,将复杂条件分支收束为数学运算,大幅提升可读性、健壮性与可维护性。










