分享一道有关find与map的趣题
分享一道关于 find 和 map 两种循环遍历处理的趣题,认识在能用map处理问题时有多么利于优化代码执行的时间复杂度。
需求
将如下格式的原数组
const arr = ["3-69", "3-87", "5-171", "5-65", "5-99"]
改造为下面格式的结果数组
const arr = [
{
id: 3,
children: ["69", "87"]
},
{
id: 5,
children: ["171", "65", "99"]
}
]
解决方法
具体的解决方法有两种,第一种使用 reduce 和 find 方法,第二种使用 reduce 和 map 方法。
第一种方法,思路比较直接,在开发中很容易想到的常用方法,但是缺点很明显,使用了嵌套循环,导致时间复杂度为 O(n^2) , 执行时间相对较长。
const arr = ["3-69", "3-87", "5-171", "5-65", "5-99"]
const result1 = arr.reduce((prev, item) => {
const [id, children] = item.split('-') //解构当前值
let t = prev.find(item => item.id === id) //查找是否与原数组有匹配id
// 存在匹配id,就推入到匹配id的 children 里面
// 如果不存在匹配id,就推入当前项到下次 prev 数组里面
// 需要注意,t的应用和 prev 里面匹配的项引用相同,可以直接赋值,并同步修改了 prev 里匹配项
t ? t.children.push(children) : prev.push({id: id, children: [children]})
return prev //返回处理好的 prev 数组
}, [])
console.log('result1 ==', result1)
第二种方法,需要理解 map 数据结构,因为 map 数据结构本身天然的优势,很适合用来通过 key 值快速查找,快速操作 key-value 结构的数据对象,因此我们可以先改造原数组对应的结果数组(改造后的数组)为 map 的数据结构,之后通过对象方法以及 map 方法得到数组结构的结果数组,其中使用了一次遍历,一次 map 方法操作,对应的时间复杂度为 O(n + n) ,也就是 O(2n) ,这样执行时间得到了大幅度的优化。
const map_data = arr.reduce((prev, item) => {
const [id, children] = item.split('-') //解构当前值
// 改造结果数组为 map 数据格式
// 不匹配id,则将id作为 key, children 作为 value,一起存入 prev 对象
// 如果匹配id,则将 children 推入到当前id下的 value 值里面
if(prev[id]){ prev[id].push(children) }
else { prev[id] = [children] }
return prev //返回处理好的 prev 对象
}, {})
// 通过对象下方法获取 map 数据结构的 key 值组成的数组
// 将该数组进行 map 方法执行,map 会创建一个新数组,将我们处理好的 map 数据结构的数据转换为数组形式
const result2 = Object.getOwnPropertyNames(map_data).map((v) => ({id: v, children: map_data[v]}))
console.log('result2 ==', result2)
至于执行的时间,可以看下面我的实际运行时间截图,可以明显发现 map 方法大幅度优化了代码的执行速度。