V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
GrapeCityChina
V2EX  ›  推广

电子表格实战锦囊:巧用稀疏数组是关键!

  •  
  •   GrapeCityChina · 2021-12-29 11:22:32 +08:00 · 538 次点击
    这是一个创建于 870 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前文中我们详细介绍过稀疏数组的那些事儿,以及在实际项目中,稀疏数组如何在前端电子表格中发挥出它最大的效果。而这次,我们将从实战应用出发,为大家介绍稀疏数组在前端中的具体应用。

    我们都知道在 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]]
    
    

    除了以上两种方式,还可以将上述方式结合,建立十字链表以应对更复杂的场景。大家如果感兴趣点个赞我们下次继续说。

    在后续的内容中,我们还会继续为大家带来其他前端电子表格技术中的深度解密,走过路过不要错过。

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3777 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 04:20 · PVG 12:20 · LAX 21:20 · JFK 00:20
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.