前言
曾经保持力扣刷题 80 天了,其中常常须要创立多维数组
比方给你两个需要:
- 创立一个 10 * 10 的数组,初值为 0
- 创立一个 10 * 10 的数组,值为 0-99
想晓得掘友们会如何创立这两个数组呢?
惯例办法
在这里展现一下常见的三种创立多维数组的办法
for 遍历
最经典的办法就是 for 循环
上面是 需要一 的代码
const arr = []
for (let i = 0; i < 10; i++) {arr[i] = []
for (let j = 0; j < 10; j++) {arr[i][j] = 0
}
}
如果要实现 需要二,只需将 arr[i][j] = 0
改为 arr[i][j] = i * 10 + j
就好了
map
我习惯应用 map 创立
需要一
const arr1 = new Array(10).fill(0).map(() => new Array(10).fill(0))
需要二
const arr2 = new Array(10).fill(0).map((_, i) => new Array(10).fill(0).map((_, j) => i * 10 + j))
因为 map 不会解决空对象,所有创立数组后得先用 fill 赋值
Array.from
我看他人的题解常常看到用 Array.from 初始化数组的
Array.from 能将一个类数组或可迭代对象转换成实在的数组,能够给第二个参数传递一个函数来批改新数组的元素,就像 map 一样
对于类数组,只有是有 length 属性的对象,就被视为类数组
需要一
const arr1 = Array.from({length: 10}, () => Array.from({ length: 10}, () => 0))
需要二
const arr2 = Array.from({length: 10}, (_, i) => Array.from({length: 10}, (_, j) => i * 10 + j))
有余
以上的三种办法 for 遍历的代码太多不想用,map 和 Array.from 有时不宜浏览
然而其余强类型语言申明多维数组都很不便,看起来也简单明了
int arr[10][10]
它们的 int 类型初值默认为 0,当然想实现 需要二 也得再做解决
所以,我就想实现一个函数,简单明了地创立与设置多维数组的初值
递归函数实现
咱们先明确函数的需要
fun(0, false, 10) // 创立一个长度为 10,初值为 0 的数组
fun(0, true, 10, 10) // 创立一个 10 * 10 的数组,初值为 0 -99
设计函数接管的参数
- 数组的初值
- 设置初值时是否须要递增
- 残余参数,示意每个维度的数组的长度
代码也不难,间接展现了:
- 如果是一维数组,就赋值,赋值时看一下要不要递增
-
如果是多维,就递归创立,在须要递增时得计算下传入的初值
function fun(value, inc, ...dimensions) { // 取首元素, 创立数组 const length = dimensions.shift() const arr = new Array(length) if (dimensions.length == 0) { // 一维数组 赋值 for (let i = 0; i < length; i++) {arr[i] = inc ? value++ : value } } else { // 多维数组 递归创立 let gap = dimensions.reduce((a, b) => a * b, 1) // 初值的距离 for (let i = 0; i < length; i++) {arr[i] = fun(value + (inc ? i * gap : 0), inc, ...dimensions) } } return arr }
链式函数实现
失败的尝试
其实呢,集体对先传初值的形式略感不爽
原本我是想创立一个链式的函数,和其余语言定义多维数组的语法统一并革新降级
fun(10) // 创立一个长度为 10,不设置初值
fun(10)(10, 0) // 创立一个 10 * 10 的数组,初值为 0
fun(10)(10)(10, 0, true) // 创立一个 10 * 10 的数组,初值为 0 -999
然而呈现了一个问题,fun 的返回值是一个数组,但它又须要作为函数调用
我尝试通过设置原型的形式让函数具备数组的性能
Object.setPrototypeOf(fun, Array.prototype)
然而函数自带不可配置的 length 属性,作为一个数组不能正确读取 length,那就无奈失常应用
解决方法
要解决这个返回值的问题,那就只能依据参数进行辨别,判断是返回函数还是返回数组
- 当传入数字为负数时,视为长度,解决数组并返回函数本身
- 当传入数字为非负数时,视为初值,将之前解决的数组赋值并返回
在数组解决时用了一些技巧,将树形的数组构造 铺平 了,上面是代码
let root = [] // 根数组 root[0] 将会是返回的后果
let tile = [root] // 平铺数组
// 函数实现
function fun(length) {if (length <= 0) {
// 完结标记 非负数
try {
// 空属性会被 JSON 转换成 null,而后再替换为初值
let res = JSON.stringify(root[0])
res = res.replaceAll('null', Math.abs(length))
return JSON.parse(res)
} finally {
// 复原变量
root = []
tile = [root]
}
} else {const next = [] // 新的平铺数组
// 遍历平铺数组, 外面的元素是倒数第二层
for (const two of tile) {
// 首次调用 two.length 为 0,但须要执行一次
for (let i = 0; i < (two.length || 1); i++) {const one = new Array(length) // 最初一层
two[i] = one
next.push(one)
}
}
// 更替平铺数组
tile = next
}
return fun
}
// 函数调用
fun(10)(0) // [0,0,0,0,0,0,0,0,0,0]
fun(2)(3)(-1) // [[1,1,1],[1,1,1]]
fun(2)(2)(2)(-2) // [[[2,2],[2,2]],[[2,2],[2,2]]]
如果想要实现初值递增,那就是再加一个参数或再加一种状况,留作读者自行实现了
应用 Proxy 实现
至此还不知足,咱们能够借助 Proxy,将函数调用改为属性拜访,更靠近强类型语言的习惯
实现一个 int 代理对象
let root = []
let tile = [root]
// 拦截器
const handers = {get(target, key) {key = parseInt(key)
if (key <= 0) {
try {let res = JSON.stringify(root[0])
res = res.replaceAll('null', Math.abs(key))
return JSON.parse(res)
} finally {root = []
tile = [root]
}
} else {const next = []
for (const two of tile) {for (let i = 0; i < (two.length || 1); i++) {const one = new Array(key)
two[i] = one
next.push(one)
}
}
tile = next
}
return int
},
}
// 用 proxy 创立 int
const int = new Proxy({}, handers)
至此,咱们就能够像那些强类型语言一样,简单明了的定义多维数组了
const arr = int[10][10][0] // 10*10 初值为 0 的数组
你们说有没有一种可能,这会成为未来的面试题呢?
结语
如果喜爱或有所帮忙的话,心愿能点赞关注,激励一下作者。
如果文章有不正确或存疑的中央,欢送评论指出。