电子表格实战锦囊:巧用稀疏数组是关键!
前文中我们详细介绍过稀疏数组的那些事儿,以及在实际项目中,稀疏数组如何在前端电子表格中发挥出它最大的效果。而这次,我们将从实战应用出发,为大家介绍稀疏数组在前端中的具体应用。
我们都知道在Javascript中是通过Array()构造函数构件稀疏矩阵,或者通过数组,设定数组的索引长度大于当前数组长度的方式来创建稀疏矩阵。
var arr = new Array(100) //arr没有元素,但arr.length是100 var a = []; //创建一个空数组,length为0 a[50] = 50; //赋值添加一个元素,length为 51
稀疏数组中,没有元素的结点为empty,获取这些结点将返回结果undefined。通过使用index in array可以判断一个结点是否有元素。例如下面代码中,a[0]和a[1]的返回都为undefined,但是a[1]其实为空。
JS中已经支持稀疏数组的存储,但在实际情况中,我们保存稀疏数组的保存并不是直接进行,而是会根据实际情况构建其他存储方式保存稀疏数组。想了解为什么要多此一举,这里就需要大家了解一个概念——数据持久化。
我们在前端进行许多操作时,会产生许多数据,例如在前端表格进行多人填报、协同的时候,会出现很多需要长期保存的数据,有些数据还要转移到其它位置中便于人们存储、管理、操作等。而实现这一目标的关键点就是数据的持久化,我们需要将内存中数据序列化为json等存储格式保存到数据库并还能反序列化到内存。在之前的文章详解电子表格中的json数据:序列化与反序列化已经具体介绍了,大家有兴趣可以查看。
看到这里,你以为问题彻底解决了吗,图样图森破。
为了解决数据持久化,我们使用了JSON,但这时新的问题也随之出现,JSON存储中没有undefined。我们对数组进行操作的时候,数组中empty字段都会序列化为null,如下图所示。
JSON.stringify(a) '[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,50]'
再次parse后,数组便不再是稀疏数组了。
JSON.parse(JSON.stringify(a)) (51) [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 50]
这种情况下,为了解决JSON数据在转化过程中上述出现的情况,我们就需要构建一些其他存储方式,来更好的地解决这个问题~而这些存储方式又有哪些特点,让我们一起看看。
1、对象存储
在前端利用JS的语言特点,我们可以通过Object可以轻松实现Sparse Array。例如在Spread JS中,对象属性名称对应所在单元格的行列,value属性保存单元格的值,同样可以拓展出formula和style等属性保存单元格公式和样式。使用Sparse Array不用初始化大小也不用关心数据的扩容,需要做行列操作时也只需要改变行列属性的引用即可。
上图中的数据存储结果如下
{ "0": { "0": { "value": 0 } }, "2": { "1": { "value": 2 }, "3": { "value": "S" } }, "4": { "3": { "value": 3 } } }
需要存取数据时候直接通过对象属性访问。下面是JS Sparse Array的一个简单对象实现。
function SparseArray(){ this._array = {} } SparseArray.prototype.setValue = function(row, col, data){ if(!this._array[row]){ this._array[row] = {} } this._array[row][col] = data } SparseArray.prototype.getValue = function(row, col){ if(this._array[row]){ return this._array[row][col] } return undefined; } let arr = new SparseArray(); arr.setValue(3, 3, 5); console.log(arr.getValue(3, 3)) // 5
2、三元组
在矩阵中每一个元素有行标,列标,元素值三个信息,将元素按需放入数组中便是三元组存储。存储结构可以是一个包含元素信息对象,也可以直接简化为一个长度为3的数组。三元组的存储方式可以方便记录类似下图的轨迹信息或者自由曲线信息,通过对数组进行push和pop,可以方便进行回退和前进。
上图中的轨迹信息,以数组三元组存储后如下,元素value代表当前已元素数量,也可以使用对象记录时间等更多信息。
[ [1,1,1], [5,8,2], [4,3,3], [1,5,4] ]
下面,我们就用这种方式建立一个undoStack记录回退。
function TSMatrix(){ this._array = []; this.undoStack = [] } TSMatrix.prototype.addNode = function(row, col, value){ this._array.push([row, col, value]) } TSMatrix.prototype.canUndo = function(){ return this._array.length > 0; } TSMatrix.prototype.undo = function(){ if(this._array.length > 0){ this.undoStack.push(this._array.pop()) } } TSMatrix.prototype.canRedo = function(){ return this.undoStack.length > 0; } TSMatrix.prototype.redo = function(){ if(this._array.length > 0){ this._array.push(this.undoStack.pop()) } } TSMatrix.prototype.print = function(){ console.log(JSON.stringify(this._array)) } let mat = new TSMatrix(); mat.addNode(1, 1, 1) mat.addNode(5, 8, 2) mat.addNode(4, 3, 3) mat.addNode(1, 5, 4) mat.print() //[[1,1,1],[5,8,2],[4,3,3],[1,5,4]] mat.undo() mat.print() //[[1,1,1],[5,8,2],[4,3,3]] mat.redo() mat.print() //[[1,1,1],[5,8,2],[4,3,3],[1,5,4]]
除了以上两种方式,还可以将上述方式结合,建立十字链表以应对更复杂的场景。大家如果感兴趣点个赞我们下次继续说。
在后续的内容中,我们还会继续为大家带来其他前端电子表格技术中的深度解密,走过路过不要错过。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
玩转 ByteBuffer
为什么要讲 Buffer 首先为什么一个小小的 Buffer 我们需要单独拎出来聊?或者说,Buffer 具体是在哪些地方被用到的呢? 例如,我们从磁盘上读取一个文件,并不是直接就从磁盘加载到内存中,而是首先会将磁盘中的数据复制到内核缓冲区中,然后再将数据从内核缓冲区复制到用户缓冲区内,在图里看起来就是这样: 再比如,我们往磁盘上写文件,也不是直接将数据写到磁盘。而是将数据从用户缓冲区写到内核缓冲区,由操作系统择机将其刷入磁盘,图跟上面这个差不多,就不画了,自行理解。 再再比如,服务器接受客户端发过来的数据时,也不是直接到用户态的 Buffer 中。而是会先从网卡到内核态的 Buffer 中,再从内核态的 Buffer 中复制到用户态的 Buffer 中。 那为什么要这么麻烦呢?复制来复制去的,首先我们用排除法排除这样做是为了好玩。 Buffer 存在的目的是为了减少与设备(例如磁盘)的交互频率,在之前的博客中也提到过「磁盘的读写是很昂贵的操作」。那昂贵在哪里呢?简单来说,和设备的交互(例如和磁盘的IO)会设计到操作系统的中断。中断需要保存之前的进程运行的上下文,中断结束之后又需要恢复...
- 下一篇
从翻硬币游戏看敏捷开发
摘要: 敏捷开发还能这么玩? 本文分享自华为云社区《从翻硬币游戏看敏捷开发》,作者:敏捷的小智。 为了推广敏捷开发的理念,很多敏捷教练都会带领团队做一些敏捷小游戏,今天咱们就来聊聊“翻硬币游戏”。 翻硬币游戏怎么玩 首先分成N个小组,每组6-10人。同时为每组准备20枚硬币。选定计时员为每组进行计时。 计时的内容分别是: 1、 第一个人翻完全部硬币的时间 2、 最后一个人接到第一枚硬币的时间 3、 最后一个人翻完全部硬币的时间 这个游戏将会做3轮,每轮每人都只能用左手翻硬币,而且一次只能翻一枚硬币。 第一轮: 一个人翻完全部硬币后,才能把硬币传递给下一个人。 第二轮: 一个人翻完5枚硬币后,才能把硬币传递给下一个人。 第三轮: 一个人翻完1枚硬币后,就能把硬币传递给下一个人。 游戏结果及意义 游戏结束以后我们就得到了每组翻硬币游戏所用时间的数据。 可以看出,无论我们采用何种方式翻硬币,第一个人翻完全部硬币的时间(Time1)始终都很接近。 随着翻硬币方式改变,变化差异巨大的时间,是Time2和Time3,即“最后一个人接到第一枚硬币的时间”与“最后一个人翻完全部硬币的时间”。这两个时间...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS关闭SELinux安全模块
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Docker使用Oracle官方镜像安装(12C,18C,19C)