Skip to content

Commit dd9d6ef

Browse files
committed
add range_as_numbers function
To get a range of values from a spreadsheet, interpreted as numbers.
1 parent 9f5045b commit dd9d6ef

2 files changed

Lines changed: 84 additions & 33 deletions

File tree

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,24 @@ The empty string is used for empty cells.
8686
* `sheet["A1:B3"]``[["a","b"],["1","2"],["","3"]]`
8787
* `sheet["A1"]``"a"`
8888

89+
### `range_as_numbers(spreadsheet, range, styles)`
90+
91+
Get the contents of the cells in the given range, parsed as numbers in the same way as the JME `parsenumber` function.
92+
93+
**Examples:**
94+
95+
The following examples use this spreadsheet:
96+
97+
<table>
98+
<tr><td>a</td><td>b</td></tr>
99+
<tr><td>1</td><td>2e2</td></tr>
100+
<tr><td></td><td>3</td></tr>
101+
</table>
102+
103+
* `range_as_numbers(sheet,"A1:B3","plain")``[[NaN,NaN], [1,NaN], [NaN,3]]`
104+
* `range_as_numbers(sheet,"A1:B3",["plain", "scientific"])``[[NaN,NaN], [1,200], [NaN,3]]`
105+
* `range_as_numbers(sheet,"A2","plain")``1`
106+
89107
### `encode_range(start_column, start_row, end_column, end_row)`
90108

91109
Given the numerical indices of the top-left and bottom-right corners of a range, return a string representing that range.

src/extension.js

Lines changed: 66 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,49 @@ Numbas.addExtension('sheets', ['display', 'util', 'jme','sheet-element', 'xlsx']
275275
constructor(wb) {
276276
this.value = wb;
277277
}
278-
}
278+
279+
/** Resolve a named reference or a string representation of a range to a 2D array of values or the value in a single cell.
280+
*
281+
* @param {string} ref
282+
* @returns {object} with either a property `range: Array.<Array.<string>>>` or `cell: string`
283+
*/
284+
resolve_ref(ref) {
285+
const wb = this.value;
286+
let range;
287+
288+
const named_ref = wb.get_named_ref(ref);
289+
if(named_ref) {
290+
range = XLSX.utils.dereference_range(named_ref);
291+
if(cells_equal(range.e,range.s)) {
292+
delete range.e;
293+
}
294+
if(range.e === undefined) {
295+
ref = XLSX.utils.encode_cell(range.s);
296+
}
297+
} else if(ref.match(/:/)) {
298+
range = XLSX.utils.decode_range(ref);
299+
} else {
300+
range = {s:XLSX.utils.decode_cell(ref)};
301+
}
302+
303+
const sheet = wb.get_worksheet(range.sheet);
304+
305+
if(range.e !== undefined) {
306+
const out = [];
307+
for(let row=range.s.r; row<=range.e.r; row++) {
308+
const orow = [];
309+
out.push(orow);
310+
for(let col=range.s.c; col<=range.e.c; col++) {
311+
const v = wb.get_cell_value(sheet, XLSX.utils.encode_cell({r:row,c:col}));
312+
orow.push((v || '')+'');
313+
}
314+
}
315+
return {range: out};
316+
} else {
317+
return {cell: (wb.get_cell_value(sheet,ref) || '')+''};
318+
}
319+
}
320+
};
279321

280322
jme.registerType(
281323
TSpreadsheet,
@@ -410,42 +452,33 @@ Numbas.addExtension('sheets', ['display', 'util', 'jme','sheet-element', 'xlsx']
410452

411453
sheets.scope.addFunction(new jme.funcObj('listval', [TSpreadsheet, TString], '?', null, {
412454
evaluate: function(args, scope) {
413-
const wb = args[0].value;
414-
let ref = args[1].value;
415-
let range;
416-
417-
const named_ref = wb.get_named_ref(ref);
418-
if(named_ref) {
419-
range = XLSX.utils.dereference_range(named_ref);
420-
if(cells_equal(range.e,range.s)) {
421-
delete range.e;
422-
}
423-
if(range.e === undefined) {
424-
ref = XLSX.utils.encode_cell(range.s);
425-
}
426-
} else if(ref.match(/:/)) {
427-
range = XLSX.utils.decode_range(ref);
428-
} else {
429-
range = {s:XLSX.utils.decode_cell(ref)};
455+
const sheet = args[0];
456+
const ref = args[1].value;
457+
const result = sheet.resolve_ref(ref);
458+
if(result.range) {
459+
return jme.wrapValue(result.range);
460+
} else if(result.cell) {
461+
return new TString(result.cell);
430462
}
463+
},
464+
random: false
465+
}));
431466

432-
433-
const sheet = wb.get_worksheet(range.sheet);
467+
sheets.scope.addFunction(new jme.funcObj('range_as_numbers', [TSpreadsheet, TString, '[string or list of string]'], '?', null, {
468+
evaluate: function(args, scope) {
469+
const sheet = args[0];
470+
const ref = args[1].value;
471+
const notationStyle = jme.unwrapValue(args[2]);
434472

473+
const result = sheet.resolve_ref(ref);
435474

436-
if(range.e !== undefined) {
437-
const out = [];
438-
for(let row=range.s.r; row<=range.e.r; row++) {
439-
const orow = [];
440-
out.push(new TList(orow));
441-
for(let col=range.s.c; col<=range.e.c; col++) {
442-
const v = wb.get_cell_value(sheet, XLSX.utils.encode_cell({r:row,c:col}));
443-
orow.push(new TString((v || '')+''));
444-
}
445-
}
446-
return new TList(out);
447-
} else {
448-
return new TString((wb.get_cell_value(sheet,ref) || '')+'');
475+
function parsenumber(s) {
476+
return Numbas.util.parseNumber(s, false, notationStyle, true);
477+
}
478+
if(result.range) {
479+
return jme.wrapValue(result.range.map(row => row.map(parsenumber)));
480+
} else if(result.cell) {
481+
return new TNum(parsenumber(result.cell));
449482
}
450483
},
451484
random: false

0 commit comments

Comments
 (0)