前言

已经坚持力扣刷题 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

设计函数接收的参数

1.数组的初值

2.设置初值时是否需要递增

3.剩余参数,表示每个维度的数组的长度

代码也不难,直接展示了:

如果是一维数组,就赋值,赋值时看一下要不要递增

如果是多维,就递归创建,在需要递增时得计算下传入的初值

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 的数组

到此这篇关于详解JavaScript如何优雅地实现创建多维数组的文章就介绍到这了,更多相关JavaScript多维数组内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

详解JavaScript如何优雅地实现创建多维数组的更多相关文章

  1. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  2. html5使用canvas实现弹幕功能示例

    这篇文章主要介绍了html5使用canvas实现弹幕功能示例的相关资料,需要的朋友可以参考下

  3. HTML5数字输入仅接受整数的实现代码

    这篇文章主要介绍了HTML5数字输入仅接受整数的实现代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  4. amaze ui 的使用详细教程

    这篇文章主要介绍了amaze ui 的使用详细教程,本文通过多种方法给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. html5简介_动力节点Java学院整理

    这篇文章主要介绍了html5简介,用于指定构建网页的元素,这些元素中的大多数都用于描述网页内容,有兴趣的可以了解一下

  6. 前端实现弹幕效果的方法总结(包含css3和canvas的实现方式)

    这篇文章主要介绍了前端实现弹幕效果的方法总结(包含css3和canvas的实现方式)的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  7. H5 canvas实现贪吃蛇小游戏

    本篇文章主要介绍了H5 canvas实现贪吃蛇小游戏,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  8. ios – parse.com用于键,预期字符串的无效类型,但是得到了数组

    我尝试将我的数据保存到parse.com.我已经预先在parse.com上创建了一个名为’SomeClass’的类.它有一个名为’mySpecialColumn’的列,其数据类型为String.这是我尝试使用以下代码保存数据的代码:如果我运行这个我得到:错误:密钥mySpecialColumn的无效类型,预期字符串,但得到数组这就是我在parse.com上的核心外观:有谁知道我为什么会收到这个错误?

  9. ios – 上下文类型’NSFastEnumeration’不能与数组文字一起使用

    斯威夫特3,你会这样做吗?解决方法正如您所发现的,您不能使用as-casting将数组文字的类型指定为NSFastEnumeration.您需要找到一个符合NSFastEnumeration的正确类,在您的情况下它是NSArray.通常写这样的东西:

  10. ios 8 Homescreen webapp,关闭和打开iPad停止javascript

    我有一个适用于iPad的全屏HTML5网络应用程序,并且刚刚安装了IOS8来试用它,它一切正常,直到你关闭并重新启动iPad.一旦web应用程序重新启动javascript就会停止并加载新页面不会重新启动它.在iPad上的Safari中打开同一页面时,关闭和打开iPad会继续按预期工作.其他人注意到了这个或想出了一个解决方案吗?解决方法这似乎是我在iOS8.1.1更新中解决的.

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部