Skip to content

Commit

Permalink
Merge pull request #100 from Kyle1297/feature/filter-formula
Browse files Browse the repository at this point in the history
Feature/filter formula
  • Loading branch information
fabiooshiro authored Nov 29, 2022
2 parents 7e83b63 + f2443f5 commit f01bb7c
Show file tree
Hide file tree
Showing 9 changed files with 145 additions and 26 deletions.
2 changes: 1 addition & 1 deletion lib/xlsx-calc.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/xlsx-calc.js.map

Large diffs are not rendered by default.

77 changes: 72 additions & 5 deletions src/Exp.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const RawValue = require('./RawValue.js');
const Range = require('./Range.js');
const str_2_val = require('./str_2_val.js');
const dynamicArrayFormulas = require('./dynamic_array_formulas.js');

const MS_PER_DAY = 24 * 60 * 60 * 1000;

Expand All @@ -24,11 +25,43 @@ module.exports = function Exp(formula) {
throw Error('#VALUE!');
}
formula.cell.v = self.calc();
if (typeof(formula.cell.v) === 'string') {
formula.cell.t = 's';
}
else if (typeof(formula.cell.v) === 'number') {
formula.cell.t = 'n';
formula.cell.t = getCellType(formula.cell.v);

if (Array.isArray(formula.cell.v && formula.cell.name && formula.cell.f && formula.cell.f.match(new RegExp(dynamicArrayFormulas.join('|'), 'i')))) {
const array = formula.cell.v;
if (!validateResultMatrix(array)) {
throw new Error('#N/A');
}

const existingCell = formula.cell.name;
const existingCellLetter = existingCell.match(/[A-Z]+/)[0];
const existingCellNumber = existingCell.match(/[0-9]+/)[0];

for (let i = 0; i < array.length; i++) {
const newCellNumber = parseInt(existingCellNumber) + i;

for (let j = 0; j < array[i].length; j++) {
const newCellValue = array[i][j];
let newCellType = getCellType(newCellValue);

// original cell
if (i === 0 && j === 0) {
formula.cell.v = newCellValue;
if (newCellType) formula.cell.t = newCellType;
}
// other cells
else {
const newLetterIndex = existingCellLetter.charCodeAt(0) - 65 + j;
const newCellLetter = getCellLetter(newLetterIndex);

const newCell = newCellLetter + newCellNumber;
formula.sheet[newCell] = {
v: newCellValue,
t: newCellType,
};
}
}
}
}
}
catch (e) {
Expand All @@ -55,6 +88,40 @@ module.exports = function Exp(formula) {
formula.status = 'done';
}
}

function getCellLetter(columnIndex) {
let newCellLetter = '';
while (newLetterIndex >= 0) {
newCellLetter = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[newLetterIndex % 26] + newCellLetter;
newLetterIndex = Math.floor(newLetterIndex / 26) - 1;
}
}

function getCellType(cellValue) {
if (typeof(cellValue) === 'string') {
return 's';
}
else if (typeof(cellValue) === 'number') {
return 'n';
}
}

function validateResultMatrix(result) {
// array must be greater than 0 and be symmetrical
if (Array.isArray(result)) {
for (let i = 0; i < result.length; i++) {
if (!(result[i] instanceof Array)) {
return false;
}
if (result[i].length !== result[0].length) {
return false;
}
}
}

return true;
}

function isEmpty(value) {
return value === undefined || value === null || value === "";
}
Expand Down
5 changes: 5 additions & 0 deletions src/dynamic_array_formulas.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const dynamicArrayFormulas = [
'FILTER',
];

module.exports = dynamicArrayFormulas;
2 changes: 1 addition & 1 deletion src/formulas-raw.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ function filter(range, condition) {
let destinationColumn = colNumber + i;
let destinationRow = rowNumber + row;
let destinationCellName = int_2_col_str(destinationColumn) + destinationRow;
console.log({ destinationCellName, row, i, v: data[row][i] })

if (sheet[destinationCellName]) {
sheet[destinationCellName].v = data[row][i];
if (destinationCellName === cellName) {
Expand Down
77 changes: 62 additions & 15 deletions test/7-formula-raw.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const XLSX_CALC = require("../src");
const assert = require('assert');

describe.only('raw formulas', () => {
describe('raw formulas', () => {

it('should filter', () => {
let workbook = {
Expand All @@ -20,30 +20,77 @@ describe.only('raw formulas', () => {
C5: {v: 'cccc'},

A9: {v: true},
B9: {v: false},
B9: {v: true},
C9: {v: false},

E3: {f: "FILTER(A3:C5,A9:C9)"},
}
}
};

XLSX_CALC(workbook);

assert.equal(workbook.Sheets.Sheet1.E3.v, 'aa');
assert.equal(workbook.Sheets.Sheet1.E4.v, 'aaa');
assert.equal(workbook.Sheets.Sheet1.E5.v, 'aaaa');
})
it('set the matrix into cells', () => {
const matrix = [
['aa', 'bb', 'cc'],
['aaa', 'bbb', 'ccc'],
['aaaa', 'bbbb', 'cccc']
];
const filter = {
SET_MATRIX: () => matrix,
}
XLSX_CALC.import_functions(filter)
let workbook = { Sheets: { Sheet1: { E3: { f: "SET_MATRIX()" } } } };
assert.equal(workbook.Sheets.Sheet1.F3.v, 'bb');
assert.equal(workbook.Sheets.Sheet1.F4.v, 'bbb');
assert.equal(workbook.Sheets.Sheet1.F5.v, 'bbbb');
});

it('should filter with no match', () => {
let workbook = {
Sheets: {
Sheet1: {
A3: {v: 'aa'},
A4: {v: 'aaa'},
A5: {v: 'aaaa'},
B3: {v: 'bb'},
B4: {v: 'bbb'},
B5: {v: 'bbbb'},
C3: {v: 'cc'},
C4: {v: 'ccc'},
C5: {v: 'cccc'},

A9: {v: false},
B9: {v: false},
C9: {v: false},

E3: {f: "FILTER(A3:C5,A9:C9)"},
}
}
};

XLSX_CALC(workbook);

assert.equal(workbook.Sheets.Sheet1.E3.v, undefined);
});

it('should filter with all match', () => {
let workbook = {
Sheets: {
Sheet1: {
A3: {v: 'aa'},
A4: {v: 'aaa'},
A5: {v: 'aaaa'},
B3: {v: 'bb'},
B4: {v: 'bbb'},
B5: {v: 'bbbb'},
C3: {v: 'cc'},
C4: {v: 'ccc'},
C5: {v: 'cccc'},

A9: {v: true},
B9: {v: true},
C9: {v: true},

E3: {f: "FILTER(A3:C5,A9:C9)"},
}
}
};

XLSX_CALC(workbook);

assert.equal(workbook.Sheets.Sheet1.E3.v, 'aa');
assert.equal(workbook.Sheets.Sheet1.E4.v, 'aaa');
assert.equal(workbook.Sheets.Sheet1.E5.v, 'aaaa');
Expand All @@ -53,5 +100,5 @@ describe.only('raw formulas', () => {
assert.equal(workbook.Sheets.Sheet1.G3.v, 'cc');
assert.equal(workbook.Sheets.Sheet1.G4.v, 'ccc');
assert.equal(workbook.Sheets.Sheet1.G5.v, 'cccc');
})
});
})
2 changes: 1 addition & 1 deletion xlsx-calc.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion xlsx-calc.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion xlsx-calc.min.js

Large diffs are not rendered by default.

0 comments on commit f01bb7c

Please sign in to comment.