diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cf79c677..501112980 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Fixed +- Fixed lookup functions rejecting array tables produced by formulas such as `IF`. - Fixed a memory leak in `LazilyTransformingAstService` where the transformations array grew unboundedly, causing increasing memory usage over time. [#1629](https://github.com/handsontable/hyperformula/issues/1629) - Fixed a memory leak in `UndoRedo` where `oldData` entries for evicted undo stack entries were never cleaned up, causing increasing memory usage over time. [#1629](https://github.com/handsontable/hyperformula/issues/1629) - Fixed the IRR function returning `#NUM!` error when the initial investment significantly exceeds the sum of returns. [#1628](https://github.com/handsontable/hyperformula/issues/1628) diff --git a/src/interpreter/plugin/BooleanPlugin.ts b/src/interpreter/plugin/BooleanPlugin.ts index a11c269b2..9cd0e87ba 100644 --- a/src/interpreter/plugin/BooleanPlugin.ts +++ b/src/interpreter/plugin/BooleanPlugin.ts @@ -6,6 +6,7 @@ import {CellError, ErrorType} from '../../Cell' import {ErrorMessage} from '../../error-message' import {ProcedureAst} from '../../parser' +import {SimpleRangeValue} from '../../SimpleRangeValue' import {InterpreterState} from '../InterpreterState' import {InternalNoErrorScalarValue, InternalScalarValue, InterpreterValue} from '../InterpreterValue' import {FunctionArgumentType, FunctionPlugin, FunctionPluginTypecheck, ImplementedFunctions} from './FunctionPlugin' @@ -135,6 +136,14 @@ export class BooleanPlugin extends FunctionPlugin implements FunctionPluginTypec * @param state */ public conditionalIf(ast: ProcedureAst, state: InterpreterState): InterpreterValue { + const conditionValue = this.evaluateAst(ast.args[0], state) + if (conditionValue instanceof SimpleRangeValue && conditionValue.isAdHoc() && conditionValue.numberOfElements() > 1) { + const vectorizedState = new InterpreterState(state.formulaAddress, true, state.formulaVertex) + return this.runFunction(ast.args, vectorizedState, this.metadata('IF'), (condition, arg2, arg3) => { + return condition ? arg2 : arg3 + }) + } + return this.runFunction(ast.args, state, this.metadata('IF'), (condition, arg2, arg3) => { return condition ? arg2 : arg3 }) diff --git a/src/interpreter/plugin/LookupPlugin.ts b/src/interpreter/plugin/LookupPlugin.ts index 00f942ddf..c64d07eb7 100644 --- a/src/interpreter/plugin/LookupPlugin.ts +++ b/src/interpreter/plugin/LookupPlugin.ts @@ -74,17 +74,11 @@ export class LookupPlugin extends FunctionPlugin implements FunctionPluginTypech */ public vlookup(ast: ProcedureAst, state: InterpreterState): InterpreterValue { return this.runFunction(ast.args, state, this.metadata('VLOOKUP'), (key: RawNoErrorScalarValue, rangeValue: SimpleRangeValue, index: number, sorted: boolean) => { - const range = rangeValue.range - - if (range === undefined) { - return new CellError(ErrorType.VALUE, ErrorMessage.WrongType) - } - if (index < 1) { return new CellError(ErrorType.VALUE, ErrorMessage.LessThanOne) } - if (index > range.width()) { + if (index > rangeValue.width()) { return new CellError(ErrorType.REF, ErrorMessage.IndexLarge) } @@ -105,16 +99,11 @@ export class LookupPlugin extends FunctionPlugin implements FunctionPluginTypech */ public hlookup(ast: ProcedureAst, state: InterpreterState): InterpreterValue { return this.runFunction(ast.args, state, this.metadata('HLOOKUP'), (key: RawNoErrorScalarValue, rangeValue: SimpleRangeValue, index: number, sorted: boolean) => { - const range = rangeValue.range - if (range === undefined) { - return new CellError(ErrorType.VALUE, ErrorMessage.WrongType) - } - if (index < 1) { return new CellError(ErrorType.VALUE, ErrorMessage.LessThanOne) } - if (index > range.height()) { + if (index > rangeValue.height()) { return new CellError(ErrorType.REF, ErrorMessage.IndexLarge) } diff --git a/test/smoke.spec.ts b/test/smoke.spec.ts index 28108ccbd..022e15bca 100644 --- a/test/smoke.spec.ts +++ b/test/smoke.spec.ts @@ -1,4 +1,4 @@ -import {HyperFormula} from '../src' +import {HyperFormula, Sheet} from '../src' import {SimpleCellAddress, simpleCellAddress} from '../src/Cell' const adr = (stringAddress: string, sheet: number = 0): SimpleCellAddress => { @@ -103,4 +103,27 @@ describe('HyperFormula', () => { hf.destroy() }) + + it('should allow VLOOKUP over IF array-constructed table', () => { + const data: Sheet = Array.from({length: 112}, () => Array(64).fill(null)) + + data[108][55] = 'K1' + data[109][55] = 'K2' + data[110][55] = 'K3' + data[111][55] = 'K4' + + data[108][47] = 100 + data[109][47] = 200 + data[110][47] = 300 + data[111][47] = 400 + + data[108][57] = 'K2' + data[108][63] = '=VLOOKUP(BF109,IF({1,0},BD109:BD112,AV109:AV112),2,0)' + + const hf = HyperFormula.buildFromArray(data, {licenseKey: 'gpl-v3'}) + + expect(hf.getCellValue({sheet: 0, row: 108, col: 63})).toBe(200) + + hf.destroy() + }) })