遍历表并保存其内容

时间:2019-11-16 22:29:38

标签: javascript

我正在用javascript阅读Excel文件。

其中一个单元格具有VLOOKUP公式:

VLOOKUP(C40,D107:F114,3,FALSE) //(searched value, table range, returned value column, exact match)

它的基本作用是在C40表范围(起点和终点)中搜索D107 -> F114上的值,然后在3列上返回该值。此示例-F列。 FALSE表示我们正在寻找完全匹配的内容。

我需要提取在公式D107:F1143列(DEF),8上定义的表单元格(107108109等)并将其内容保存在代码中。

这是我所做的-很好,我想知道是否有更短的方法来做到这一点:

  const formula = sheet.cell('A40').formula()
  var splittedFormula = formula.split(/[\s,]+/) //split formula by params -> splittedFormula = [C40, D107:F114, 3, FALSE]
  const numOfColumns = splittedFormula[2] //geting the total number of columns by the third param- "returned value column" -> numOfColumns = 3
  let numOfCells = splittedFormula[1].split(/[\s:]+/) //get the start point of the table -> numOfCells = [D107, F114]
  let startingCell = numOfCells[0].replace(/\D/g, '') //extract the starting cell -> startingCell = 107
  let startingColumn = numOfCells[0].replace(/\d+/g, '') //extract the starting column -> startingColumn = D
  numOfCells =
    numOfCells[1].replace(/\D/g, '') - numOfCells[0].replace(/\D/g, '') + 1 //calculate how many cells on each column by substracting the start point cell from the end point cell (`114 - 107`) -> numOfCells = 8
  var table = new Array(numOfColumns) //defining an array of arrays -> table = [3]
  let currentCell
  //loop through the table in the excel sheet and save it's content each column is an array that store the cells value. table = [3][8] 
  for (var i = 0; i < numOfColumns; i++) { //numOfColumns=3
    table[i] = new Array(numOfCells) //numOfCells=8
    currentCell = startingCell
    for (var j = 0; j < numOfCells; j++) {
      table[i][j] = sheet.cell(startingColumn + currentCell).value()
      currentCell = parseFloat(currentCell) + 1 //increment to the next cell i.e. 107 + 1 = 108 etc..
    }
    startingColumn = String.fromCharCode(startingColumn.charCodeAt() + 1) //increment to the next column i.e. D + 1 = F etc..
  }

1 个答案:

答案 0 :(得分:1)

第一个“最小化”(也是最重要的一个)是检查您的sheet实例是否允许提取切片(等同于您尝试提取的子矩阵或表)。

您可以通过更通用的方式

  1. 跳过一些有关提取公式的内容(或至少使其更清楚)
let formula = 'D107:F114'
let [colStart, rowStart, colEnd, rowEnd] = formula.match(/([A-Z]+)(\d+):([A-Z]+)(\d+)/).slice(1);
//[ colStart='D', rowStart='107', colEnd='F', rowEnd='114' ]
rowStart = parseInt(rowStart);
rowEnd   = parseInt(rowEnd);
colStart = colStart.charCodeAt()-65;
colEnd   = colEnd.charCodeAt()-65;

请注意,这里您可能有AA或ZZ之类的列,因此您可能希望相应地适应colStartcolEnd的转换

  1. 然后使用map代替for循环
let table = Array(colEnd-colStart+1).fill(0).map((_, j)=>{
    return Array(rowEnd-rowStart+1).fill(0).map((_, i)=>{
        let col = colStart + j;
        let row = rowStart + i;
        return sheet.cell(String.fromCharCode(col+65)+row).value();
    })
})

关于AA或ZZ,下面是一种将它们来回转换为int的算法,但是再次可能依赖于您的库,因为无论如何它都必须解析您的字符串。

let sheet = {
    cell(x){return {value(){return x}}}
}

//not used, but enough to handle single letters...
function toColIdx(s){
    return s.charCodeAt()-'A'.charCodeAt();
}

function idxToCol(idx){
    return String.fromCharCode(idx+'A'.charCodeAt())
}

let base = (function(){
    let idxToChar = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
    let charToIdx   = idxToChar.reduce((acc, f,i)=>(acc[f] = i+1, acc),{})
    //I have checked the identity of idxToCol == idxToCol(toColIdx(idxToCol)) up to 16384
    return {
        toColIdx(s){
            return s.split('')
                .reverse()
                .reduce((acc,c,i)=>acc+charToIdx[c]*Math.pow(26,i),0);
        },
        idxToCol(idx){
            if(idx==1)return 'A';
            let n = Math.ceil(Math.log(idx)/Math.log(26));
            s = '';
            for(let i = 0; i<n; ++i){
                let x = idx % 26;
                if(x != 0){
                    s = idxToChar[x-1] + s;
                    idx-=x;
                    idx /= 26;
                }else{
                    s = 'Z' + s;
                    idx-=26;
                    idx /= 26;
                    if(idx==0) return s;
                }
            }
            return s;
        }
    }
})();
function extract(sheet, formula){
    let [colStart, rowStart, colEnd, rowEnd] = formula.match(/([A-Z]+)(\d+):([A-Z]+)(\d+)/).slice(1);
    //[ colStart='D', rowStart='107', colEnd='F', rowEnd='114' ]
    rowStart = parseInt(rowStart);
    rowEnd   = parseInt(rowEnd);
    colStart = base.toColIdx(colStart)
    colEnd   = base.toColIdx(colEnd)
    return Array(colEnd-colStart+1).fill(0).map((_, j)=>{
        return Array(rowEnd-rowStart+1).fill(0).map((_, i)=>{
            let col = colStart + j;
            let row = rowStart + i;
            return sheet.cell(base.idxToCol(col)+row).value();
        })
    })   
}
console.log(JSON.stringify(extract(sheet, 'A107:D114'),null,2))
console.log(JSON.stringify(extract(sheet, 'BZ107:CA114'),null,2))