UNPKG

hyperformula

Version:

HyperFormula is a JavaScript engine for efficient processing of spreadsheet-like data and formulas

75 lines 3.31 kB
/** * @license * Copyright (c) 2025 Handsoncode. All rights reserved. */ import { getRawValue } from "../interpreter/InterpreterValue.mjs"; import { forceNormalizeString } from "../interpreter/ArithmeticHelper.mjs"; import { compare, findLastOccurrenceInOrderedRange } from "../interpreter/binarySearch.mjs"; const NOT_FOUND = -1; export class AdvancedFind { constructor(dependencyGraph) { this.dependencyGraph = dependencyGraph; } advancedFind(keyMatcher, rangeValue, { returnOccurrence } = { returnOccurrence: 'first' }) { const range = rangeValue.range; const values = range === undefined ? rangeValue.valuesFromTopLeftCorner() : this.dependencyGraph.computeListOfValuesInRange(range); const initialIterationIndex = returnOccurrence === 'first' ? 0 : values.length - 1; const iterationCondition = returnOccurrence === 'first' ? i => i < values.length : i => i >= 0; const incrementIndex = returnOccurrence === 'first' ? i => i + 1 : i => i - 1; for (let i = initialIterationIndex; iterationCondition(i); i = incrementIndex(i)) { if (keyMatcher(getRawValue(values[i]))) { return i; } } return NOT_FOUND; } basicFind(searchKey, rangeValue, searchCoordinate, { ordering, ifNoMatch, returnOccurrence }) { const normalizedSearchKey = typeof searchKey === 'string' ? forceNormalizeString(searchKey) : searchKey; const range = rangeValue.range; if (range === undefined) { return this.findNormalizedValue(normalizedSearchKey, rangeValue.valuesFromTopLeftCorner(), ifNoMatch, returnOccurrence); } if (ordering === 'none') { return this.findNormalizedValue(normalizedSearchKey, this.dependencyGraph.computeListOfValuesInRange(range), ifNoMatch, returnOccurrence); } return findLastOccurrenceInOrderedRange(normalizedSearchKey, range, { searchCoordinate, orderingDirection: ordering, ifNoMatch }, this.dependencyGraph); } findNormalizedValue(searchKey, searchArray, ifNoMatch = 'returnNotFound', returnOccurrence = 'first') { const normalizedArray = searchArray.map(getRawValue).map(val => typeof val === 'string' ? forceNormalizeString(val) : val); if (ifNoMatch === 'returnNotFound') { return returnOccurrence === 'first' ? normalizedArray.indexOf(searchKey) : normalizedArray.lastIndexOf(searchKey); } const compareFn = ifNoMatch === 'returnLowerBound' ? (left, right) => compare(left, right) : (left, right) => -compare(left, right); let bestValue = ifNoMatch === 'returnLowerBound' ? -Infinity : Infinity; let bestIndex = NOT_FOUND; const initialIterationIndex = returnOccurrence === 'first' ? 0 : normalizedArray.length - 1; const iterationCondition = returnOccurrence === 'first' ? i => i < normalizedArray.length : i => i >= 0; const incrementIndex = returnOccurrence === 'first' ? i => i + 1 : i => i - 1; for (let i = initialIterationIndex; iterationCondition(i); i = incrementIndex(i)) { const value = normalizedArray[i]; if (value === searchKey) { return i; } if (compareFn(value, searchKey) > 0) { continue; } if (compareFn(bestValue, value) < 0) { bestValue = value; bestIndex = i; } } return bestIndex; } }