Spreadsheet actual combat bag: skillfully using sparse array is the key!

Posted by laduch on Sun, 02 Jan 2022 14:13:25 +0100

We described it in detail earlier Sparse arrays , and how the sparse array can exert its maximum effect in the front-end spreadsheet in the actual project. This time, we will introduce the specific application of sparse array in the front end from the practical application.

We all know that in Javascript, the sparse matrix is created by constructing the sparse matrix through the Array() constructor or by setting the index length of the array to be greater than the current array length.

var arr = new Array(100)   //Arr has no element, but arr.length is 100
var a = [];  //Create an empty array with length 0
a[50] = 50;  //The assignment adds an element with a length of 51
 

In a sparse array, nodes without elements are empty. Obtaining these nodes will return the result undefined. By using index in array, you can determine whether a node has elements. For example, in the following code, the returns of a[0] and a[1] are undefined, but a[1] is actually empty.

JS already supports the storage of sparse arrays, but in practice, we do not save sparse arrays directly, but build other storage methods to save sparse arrays according to the actual situation. To understand why we should do this, we need to understand a concept - data persistence.

When we perform many operations at the front end, many data will be generated. For example, when multiple people fill in and cooperate in the front-end form, there will be many data that need to be saved for a long time, and some data will be transferred to other locations for people's storage, management and operation. The key point to achieve this goal is data persistence. We need to serialize the data in memory into json and other storage formats, save it to the database, and deserialize it to memory. In previous articles Explain the in the spreadsheetjson data: serialization and deserialization It has been introduced in detail. You can check it if you are interested.

See here, do you think the problem is completely solved, Tucson.

In order to solve the problem of data persistence, we use JSON, but a new problem also arises. There is no undefined in JSON storage. When we operate on the array, the empty field in the array will be serialized to null, as shown in the following figure.

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]'

After parse again, the array is no longer sparse.

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]

In this case, in order to solve the above problems in the conversion of JSON data, we need to build some other storage methods to better solve this problem ~ and let's have a look at the characteristics of these storage methods.

1. Object storage

At the front end, we can easily implement Sparse Array through Object by using the language characteristics of JS. For example, in Spread JS, the Object attribute name corresponds to the row and column of the cell, and the value attribute saves the value of the cell. Similarly, the formula and style attributes can be expanded to save the cell formula and style. When using Sparse Array, you do not need to initialize the size or care about the expansion of data. When you need to perform row and column operations, you only need to change the reference of row and column attributes.

The data storage results in the figure above are as follows

{
    "0": {
        "0": {
            "value": 0
        }
    },
    "2": {
        "1": {
            "value": 2
        },
        "3": {
            "value": "S"
        }
    },
    "4": {
        "3": {
            "value": 3
        }
    }
}
 

When you need to access data, you can access it directly through object properties. The following is a simple object implementation of 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. Triplet

In the matrix, each element has three information: row mark, column mark and element value. Putting the elements into the array as needed is triple storage. The storage structure can be an object containing element information, or it can be directly reduced to an array with a length of 3. The triple storage method can easily record track information or free curve information similar to the figure below. It is convenient to go back and forward by push ing and pop the array.

The track information in the figure above is stored in array triples as follows. The element value represents the current number of elements. You can also use the object to record time and other more information.

[
    [1,1,1],
    [5,8,2],
    [4,3,3], 
    [1,5,4]
]

Next, let's create an undo stack record fallback in this way.

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]]

In addition to the above two methods, you can also combine the above methods to establish a cross linked list to deal with more complex scenarios. If you are interested, give us a compliment. Let's continue next time.

In the following content, we will continue to bring you in-depth decryption in other front-end spreadsheet technologies. Don't miss it when you pass by.

Topics: Javascript spreadjs