Ramda 读 xls

101 阅读1分钟

目标

  1. <input type="file"> 触发 change 事件。
  2. 在监听器用 xlsx 把工作表当成 键值对数组 读取。
  3. 转成期望格式。

xlsx@0.18.5

Excel 工作表预览

image.png

读取

image.png

成品

键为姓名,值为年龄。

姓名年龄字典

思路

  1. 得到行号数组,从首行行号(不算表头)到末行行号。
  2. 行号数组转换
    1. 键值对数组 [[sheet1.A1.v, sheet1.B1.v], [sheet1.A2.v, sheet1.B2.v] ...]
    2. Record<Name, Age>{ name: string; age: number; }[]

行号数组

思路

  1. 末行行号
    1. Sheet1['!ref']
    2. 取末行行号字符串
    3. Number
  2. R.Range(start, end)

末行行号

const RowCount = R.compose( Number, LastRowNum, R.prop('!ref') );

取末行行号字符串

// 方案一 `R.slice()`
const LastRowNum = R.converge(
  R.slice(R.__, Number.POSITIVE_INFINITY, R.__),
  [
    R.compose( R.add(2), R.indexOf(':') ),
    R.identity
  ]
);

// 方案二 `R.match()`
const LastRowNum = R.compose( R.prop(1), R.match(/A\d+:B(\d+)/) );

行号数组转对象

转键值对数组

const PairOf = sheet1 => rowNum => R.map(
  R.compose(
    R.path(R.__, sheet1),
    R.pair(R.__, 'v'),
    R.concat(R.__, String(rowNum))
  ),
  ['A', 'B']
);

export const Pairs = R.converge(R.map, [
  PairOf,
  R.compose( R.range(2), R.inc, RowCount )
]);

rowNum = 2,转换流程如下:

  1. R.concat(R.__, String(rowNum))

    ['A', 'B'] -> ['A2', 'B2'] 工作表单元格位置。

  2. R.pair(R.__, 'v')

    [['A2', 'v'], ['B2', v]] sheet1 读属性路径。

  3. R.path(R.__, sheet1)

    行号对应的键值对。

转对象

R.fromPairs() 可以当成 Object.fromEntries()

R.compose( R.fromPairs, Pairs )(sheet1);

监听器

import { read } from 'xlsx/xlsx.mjs';
import { Pairs } from '@/util/xls.js';

async function handleChange(event) {
  const elem = event.currentTarget;
  
  const dic = await new Promise(resolve => {
    const fr = new FileReader();
    fr.readAsArrayBuffer(elem.files[0]);
    fr.addEventListener('load', 
      R.compose(
        resolve,
        R.fromPairs,
        Pairs,
        R.path(['Sheets', 'Sheet1']),
        read,
        R.path(['target', 'result'])
      )
    );
  });
  
  elem.value = '';
}

行号数组转对象数组

只需换个函数,各键值对传入 R.zipObj()

R.zipObj(['name', 'age'], ['赵志刚', 20]);
// {"age": 20, "name": "赵志刚"}
  fr.addEventListener('load', 
    R.compose(
      resolve,
-     R.fromPairs,
+     R.map(R.zipObj(['name', 'age'])),
      Pairs,
      R.path(['Sheets', 'Sheet1']),
      read,
      R.path(['target', 'result'])
    )
  );