如何优雅高效地按索引区间将一维数组分割为多维数组?
问题:根据指定索引区间分割一维数组为二维数组
给定存储需移除元素索引区间的二维数组trim,需要找到一种优雅且高性能的方法,根据这些区间将一维数组words分割为目标二维数组。示例如下:
// 传入的trim参数 var trim = [ [3, 4], // 移除索引3、4的两个元素 [9, 10] // 移除索引9、10的两个元素 ]; var words = [ { word: "hello", st: 0 }, { word: "stack-overflow", st: 0.5 }, { word: "lets", st: 1 }, { word: "remove", st: 1.5 }, { word: "some", st: 2 }, { word: "words", st: 2.5 }, { word: "efficiently", st: 3 }, { word: "lets", st: 3.5 }, { word: "do", st: 4 }, { word: "it", st: 4.5 }, { word: "yay", st: 5 } ]; // 期望得到的结果 var result = [ [ { word: "hello", st: 0 }, { word: "stack-overflow", st: 0.5 }, { word: "lets", st: 1 } ], [ { word: "words", st: 2.5 }, { word: "efficiently", st: 3 }, { word: "lets", st: 3.5 }, { word: "do", st: 4 } ] ];
我目前用reduce实现的代码只能过滤掉指定区间的元素,无法生成目标二维数组:
words.reduce((acc, curr, i) => { const wordBetween = trim.some(t => { return t[0] <= i && t[1] >= i }); console.log({wordBetween, curr}) if (wordBetween) { return acc; } return [...acc, curr] }, [])
解决方案
方法一:高效切片法(推荐,性能更优)
通过计算所有需要保留的区间,利用数组原生slice方法批量提取元素,适合大数据量场景:
function splitByTrimIntervals(words, trim) { if (trim.length === 0) return [words]; // 先对trim区间按起始索引排序,避免乱序导致错误 const sortedTrim = [...trim].sort((a, b) => a[0] - b[0]); const result = []; let currentStart = 0; for (const [trimStart, trimEnd] of sortedTrim) { // 提取当前保留段:从currentStart到trimStart的前一位 if (currentStart < trimStart) { result.push(words.slice(currentStart, trimStart)); } // 更新起始位置为移除区间的下一位 currentStart = trimEnd + 1; // 如果起始位置超出数组长度,提前终止循环 if (currentStart >= words.length) break; } // 处理最后一段未被覆盖的元素 if (currentStart < words.length) { result.push(words.slice(currentStart)); } return result; } // 调用测试 console.log(splitByTrimIntervals(words, trim));
方法二:遍历跟踪法(逻辑直观)
通过遍历数组,实时跟踪当前收集的子数组,遇到移除区间时完成分割:
function splitByTrimIntervals(words, trim) { const sortedTrim = [...trim].sort((a, b) => a[0] - b[0]); const result = []; let currentSegment = []; let trimIndex = 0; let currentTrim = sortedTrim[trimIndex]; for (let i = 0; i < words.length; i++) { // 检查当前索引是否在移除区间内 if (currentTrim && i >= currentTrim[0] && i <= currentTrim[1]) { // 如果当前子数组有元素,存入结果并清空 if (currentSegment.length > 0) { result.push(currentSegment); currentSegment = []; } // 到达移除区间末尾时,切换到下一个移除区间 if (i === currentTrim[1]) { trimIndex++; currentTrim = sortedTrim[trimIndex]; } } else { // 不在移除区间,加入当前子数组 currentSegment.push(words[i]); } } // 遍历结束后,把最后一段非空子数组存入结果 if (currentSegment.length > 0) { result.push(currentSegment); } return result; } // 调用测试 console.log(splitByTrimIntervals(words, trim));
核心思路说明
两种方法都先对trim区间排序,确保处理顺序正确。区别在于:
- 切片法通过计算保留区间的起始和结束位置,批量提取元素,原生
slice方法性能更高 - 遍历法逐元素处理,逻辑更直观,适合需要对元素做额外处理的场景
你的原reduce代码只实现了元素过滤,没有处理分割逻辑。上述方法通过跟踪分割时机(遇到移除区间时),将之前收集的元素作为独立子数组存入结果,从而得到目标二维数组。
内容的提问来源于stack exchange,提问作者Michael Joseph Aubry
相关产品推荐
相关产品推荐

