Home About
JavaScript , Node.js , Excel

Node.js での 巨大なエクセルデータ読み込み

前回( Node.js での エクセルデータ読み込み )で、エクセルデータが簡単に扱えると思っていたが、 実践的なデータを使って前のコードで処理したところ、ハングアップしてしまい作動しない. よくコードを見てみると xlsx.utils.sheet_to_json として、エクセルデータ全体を json object に変換している. 推測ではあるがおそらくこれが原因であろう.

今回はそのような巨大なデータをこの xlsx モジュールで処理する方法を備忘録として書きます.
なお、ここに書いたことは SheetJS のページ を読めばすぐわかる程度の情報なので、あしからず.

アドレス指定して該当のセルを取得

example xlsx data

たとえば上の画像の B2 のセルには「ポテトチップス うすしお味」という文字列が入っていますが、これを取得するには:

const xlsx = require('xlsx')

const workbook = xlsx.readFile('potate.xlsx');
const sheetNames = workbook.SheetNames;
const worksheet = workbook.Sheets[sheetNames[0]];

const cellAddress = 'B2';
const cell = worksheet[cellAddress];

console.log(cell.w);

なお、potate.xlsx はこちらからダウンロード可能です.

B2 などの文字列を作り出すには

The Common Spreadsheet Format (CSF) なるものがあるらしく、 B2 ならば 2カラム目の2行目のセルなので {c:1, r:1} と表現する( 0-index 方式な点に注意). これを xlsx.utils.encode_cell() 関数を使うことで B2 という文字列を得ることができる.

つまり:

const cellAddress = 'B2';

と書いていたところを:

const cellAddress = xlsx.utils.encode_cell( {c:1, r:1} );

と、書けることになる.

言うまでもないことだが、このように CSF でセル位置を特定できれば、格段にプログラマティカリーにセルにアクセスできる.

品名列の情報だけを取り出したければ

要するに B列の 1,2,3 行目のセルの内容を取り出せばよいので:

[1,2,3].forEach( (rowIndex)=> {
    const cellAddress = xlsx.utils.encode_cell( {c:1, r:rowIndex} );
    const cell = worksheet[cellAddress];
    console.log(cell.w);
});

と書けばよい.

そして、たとえば、(ムカつくことに)誰かが品名列を C 列やまたはもっと別の列に移動してしまったとしても、 品名列をB列と決め打ちしないで、プログラムで探すようにしておけば問題ない.

品名がどの列に移動しようが、その列番号を見つける function:

const findProductNameColumnIndex = (xlsx, worksheet)=>{
    let retVal = null;

    [...Array(100).keys()].forEach( (columnIndex)=> {
        const cellAddress = xlsx.utils.encode_cell( {c:columnIndex, r:0} );
        const cell = worksheet[cellAddress];
        if( cell!=undefined && cell.w=='品名' ){
            retVal = columnIndex;
        }
    });

    return retVal;
};

その function を使って、ダイナミックに品名の存在する列番号を解決して品名情報を取り出す:

const productNameColumnIndex = findProductNameColumnIndex(xlsx, worksheet);
[1,2,3].forEach( (rowIndex)=> {
    const cellAddress = xlsx.utils.encode_cell( {c:productNameColumnIndex, r:rowIndex} );
    const cell = worksheet[cellAddress];
    console.log(cell.w);
});

結果はこれ:

ポテトチップス うすしお味
ポテトチップス コンソメパンチ
ポテトチップス のり塩

まとめ

xlsx.utils.sheet_to_json が機能しないような巨大なエクセルデータを処理する場合でも CSF 形式でプログラマティカリーに取り出したいセル位置を計算して、必要な情報をエクセルデータから取り出すことができる.