diff --git a/CHANGELOG.md b/CHANGELOG.md index 787c642ad9..100896df8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Added - Added support for the new license key format. A proprietary key can now grant a subset of the library: a function your key does not include evaluates to a `#LIC!` error, and the matching parts of the API throw a `LicenseCapabilityMissingError`. `getAvailableFunctions()` and `getFunctionDetails()` describe only the functions your key includes. [#1728](https://github.com/handsontable/hyperformula/pull/1728) +- Added new functions: RANK.EQ and its alias RANK. [#1786](https://github.com/handsontable/hyperformula/pull/1786) ### Changed diff --git a/src/i18n/languages/csCZ.ts b/src/i18n/languages/csCZ.ts index fea286083c..a1dfd82f48 100644 --- a/src/i18n/languages/csCZ.ts +++ b/src/i18n/languages/csCZ.ts @@ -387,6 +387,8 @@ const dictionary: RawTranslationPackage = { IMTAN: 'IMTAN', LARGE: 'LARGE', SMALL: 'SMALL', + 'RANK.EQ': 'RANK.EQ', + RANK: 'RANK', PERCENTILE: 'PERCENTIL', 'PERCENTILE.INC': 'PERCENTIL.INC', 'PERCENTILE.EXC': 'PERCENTIL.EXC', diff --git a/src/i18n/languages/daDK.ts b/src/i18n/languages/daDK.ts index 9264551249..8ac788fb9d 100644 --- a/src/i18n/languages/daDK.ts +++ b/src/i18n/languages/daDK.ts @@ -387,6 +387,8 @@ const dictionary: RawTranslationPackage = { IMTAN: 'IMAGTAN', LARGE: 'STØRSTE', SMALL: 'MINDSTE', + 'RANK.EQ': 'PLADS.LIGE', + RANK: 'PLADS', PERCENTILE: 'FRAKTIL', 'PERCENTILE.INC': 'FRAKTIL.MEDTAG', 'PERCENTILE.EXC': 'FRAKTIL.UDELAD', diff --git a/src/i18n/languages/deDE.ts b/src/i18n/languages/deDE.ts index 4d3d39319b..0322878f99 100644 --- a/src/i18n/languages/deDE.ts +++ b/src/i18n/languages/deDE.ts @@ -387,6 +387,8 @@ const dictionary: RawTranslationPackage = { IMTAN: 'IMATAN', LARGE: 'KGRÖSSTE', SMALL: 'KKLEINSTE', + 'RANK.EQ': 'RANG.GLEICH', + RANK: 'RANG', PERCENTILE: 'QUANTIL', 'PERCENTILE.INC': 'QUANTIL.INKL', 'PERCENTILE.EXC': 'QUANTIL.EXKL', diff --git a/src/i18n/languages/enGB.ts b/src/i18n/languages/enGB.ts index d271b732cd..616968d4a8 100644 --- a/src/i18n/languages/enGB.ts +++ b/src/i18n/languages/enGB.ts @@ -387,6 +387,8 @@ const dictionary: RawTranslationPackage = { IMTAN: 'IMTAN', LARGE: 'LARGE', SMALL: 'SMALL', + 'RANK.EQ': 'RANK.EQ', + RANK: 'RANK', PERCENTILE: 'PERCENTILE', 'PERCENTILE.INC': 'PERCENTILE.INC', 'PERCENTILE.EXC': 'PERCENTILE.EXC', diff --git a/src/i18n/languages/esES.ts b/src/i18n/languages/esES.ts index 9c6b817b98..02dec62253 100644 --- a/src/i18n/languages/esES.ts +++ b/src/i18n/languages/esES.ts @@ -387,6 +387,8 @@ export const dictionary: RawTranslationPackage = { IMTAN: 'IMTAN', LARGE: 'K.ESIMO.MAYOR', SMALL: 'K.ESIMO.MENOR', + 'RANK.EQ': 'JERARQUIA.EQV', + RANK: 'JERARQUIA', PERCENTILE: 'PERCENTIL', 'PERCENTILE.INC': 'PERCENTIL.INC', 'PERCENTILE.EXC': 'PERCENTIL.EXC', diff --git a/src/i18n/languages/fiFI.ts b/src/i18n/languages/fiFI.ts index 99ff05708f..0ce4dd0adb 100644 --- a/src/i18n/languages/fiFI.ts +++ b/src/i18n/languages/fiFI.ts @@ -387,6 +387,8 @@ const dictionary: RawTranslationPackage = { IMTAN: 'KOMPLEKSI.TAN', LARGE: 'SUURI', SMALL: 'PIENI', + 'RANK.EQ': 'RANK.EQ', + RANK: 'RANK', PERCENTILE: 'PROSENTTIPISTE', 'PERCENTILE.INC': 'PROSENTTIPISTE.SIS', 'PERCENTILE.EXC': 'PROSENTTIPISTE.ULK', diff --git a/src/i18n/languages/frFR.ts b/src/i18n/languages/frFR.ts index f7b9be45f7..f16a4333fc 100644 --- a/src/i18n/languages/frFR.ts +++ b/src/i18n/languages/frFR.ts @@ -387,6 +387,8 @@ const dictionary: RawTranslationPackage = { IMTAN: 'COMPLEXE.TAN', LARGE: 'GRANDE.VALEUR', SMALL: 'PETITE.VALEUR', + 'RANK.EQ': 'EQUATION.RANG', + RANK: 'RANG', PERCENTILE: 'CENTILE', 'PERCENTILE.INC': 'CENTILE.INCLURE', 'PERCENTILE.EXC': 'CENTILE.EXCLURE', diff --git a/src/i18n/languages/huHU.ts b/src/i18n/languages/huHU.ts index e40219553a..cb963d8bd4 100644 --- a/src/i18n/languages/huHU.ts +++ b/src/i18n/languages/huHU.ts @@ -387,6 +387,8 @@ const dictionary: RawTranslationPackage = { IMTAN: 'KÉPZ.TAN', LARGE: 'NAGY', SMALL: 'KICSI', + 'RANK.EQ': 'RANK.EQ', + RANK: 'RANK', PERCENTILE: 'PERCENTILIS', 'PERCENTILE.INC': 'PERCENTILIS.TARTALMAZ', 'PERCENTILE.EXC': 'PERCENTILIS.KIZÁR', diff --git a/src/i18n/languages/idID.ts b/src/i18n/languages/idID.ts index 719cb11db6..21f5fb5fee 100644 --- a/src/i18n/languages/idID.ts +++ b/src/i18n/languages/idID.ts @@ -387,6 +387,8 @@ const dictionary: RawTranslationPackage = { IMTAN: 'IMTAN', LARGE: 'BESAR', SMALL: 'KECIL', + 'RANK.EQ': 'RANK.EQ', + RANK: 'RANK', PERCENTILE: 'PERCENTILE', 'PERCENTILE.INC': 'PERCENTILE.INC', 'PERCENTILE.EXC': 'PERCENTILE.EXC', diff --git a/src/i18n/languages/itIT.ts b/src/i18n/languages/itIT.ts index 1a494628f8..161ee522ae 100644 --- a/src/i18n/languages/itIT.ts +++ b/src/i18n/languages/itIT.ts @@ -387,6 +387,8 @@ const dictionary: RawTranslationPackage = { IMTAN: 'COMP.TAN', LARGE: 'GRANDE', SMALL: 'PICCOLO', + 'RANK.EQ': 'RANGO.UG', + RANK: 'RANGO', PERCENTILE: 'PERCENTILE', 'PERCENTILE.INC': 'INC.PERCENTILE', 'PERCENTILE.EXC': 'ESC.PERCENTILE', diff --git a/src/i18n/languages/nbNO.ts b/src/i18n/languages/nbNO.ts index ff15415a98..404467e801 100644 --- a/src/i18n/languages/nbNO.ts +++ b/src/i18n/languages/nbNO.ts @@ -387,6 +387,8 @@ const dictionary: RawTranslationPackage = { IMTAN: 'IMTAN', LARGE: 'N.STØRST', SMALL: 'N.MINST', + 'RANK.EQ': 'RANG.EKV', + RANK: 'RANG', PERCENTILE: 'PERSENTIL', 'PERCENTILE.INC': 'PERSENTIL.INK', 'PERCENTILE.EXC': 'PERSENTIL.EKS', diff --git a/src/i18n/languages/nlNL.ts b/src/i18n/languages/nlNL.ts index c491e2f44b..c4a1568d81 100644 --- a/src/i18n/languages/nlNL.ts +++ b/src/i18n/languages/nlNL.ts @@ -387,6 +387,8 @@ const dictionary: RawTranslationPackage = { IMTAN: 'C.TAN', LARGE: 'GROOTSTE', SMALL: 'KLEINSTE', + 'RANK.EQ': 'RANG.GELIJK', + RANK: 'RANG', PERCENTILE: 'PERCENTIEL', 'PERCENTILE.INC': 'PERCENTIEL.INC', 'PERCENTILE.EXC': 'PERCENTIEL.EXC', diff --git a/src/i18n/languages/plPL.ts b/src/i18n/languages/plPL.ts index 6fa4f016b4..1bd8231f71 100644 --- a/src/i18n/languages/plPL.ts +++ b/src/i18n/languages/plPL.ts @@ -387,6 +387,8 @@ const dictionary: RawTranslationPackage = { IMTAN: 'TAN.LICZBY.ZESP', LARGE: 'MAX.K', SMALL: 'MIN.K', + 'RANK.EQ': 'POZYCJA.NAJW', + RANK: 'POZYCJA', PERCENTILE: 'PERCENTYL', 'PERCENTILE.INC': 'PERCENTYL.PRZEDZ.ZAMK', 'PERCENTILE.EXC': 'PERCENTYL.PRZEDZ.OTW', diff --git a/src/i18n/languages/ptPT.ts b/src/i18n/languages/ptPT.ts index 6c1e68c328..5272290d62 100644 --- a/src/i18n/languages/ptPT.ts +++ b/src/i18n/languages/ptPT.ts @@ -387,6 +387,8 @@ const dictionary: RawTranslationPackage = { IMTAN: 'IMTAN', LARGE: 'MAIOR', SMALL: 'MENOR', + 'RANK.EQ': 'ORDEM.EQ', + RANK: 'ORDEM', PERCENTILE: 'PERCENTIL', 'PERCENTILE.INC': 'PERCENTIL.INC', 'PERCENTILE.EXC': 'PERCENTIL.EXC', diff --git a/src/i18n/languages/ruRU.ts b/src/i18n/languages/ruRU.ts index 6b35c1ce2f..d8d99a1284 100644 --- a/src/i18n/languages/ruRU.ts +++ b/src/i18n/languages/ruRU.ts @@ -387,6 +387,8 @@ const dictionary: RawTranslationPackage = { IMTAN: 'МНИМ.TAN', LARGE: 'НАИБОЛЬШИЙ', SMALL: 'НАИМЕНЬШИЙ', + 'RANK.EQ': 'РАНГ.РВ', + RANK: 'РАНГ', PERCENTILE: 'ПЕРСЕНТИЛЬ', 'PERCENTILE.INC': 'ПРОЦЕНТИЛЬ.ВКЛ', 'PERCENTILE.EXC': 'ПРОЦЕНТИЛЬ.ИСКЛ', diff --git a/src/i18n/languages/svSE.ts b/src/i18n/languages/svSE.ts index 5f87580087..0a5a3abc34 100644 --- a/src/i18n/languages/svSE.ts +++ b/src/i18n/languages/svSE.ts @@ -387,6 +387,8 @@ const dictionary: RawTranslationPackage = { IMTAN: 'IMTAN', LARGE: 'STÖRSTA', SMALL: 'MINSTA', + 'RANK.EQ': 'RANG.EKV', + RANK: 'RANG', PERCENTILE: 'PERCENTIL', 'PERCENTILE.INC': 'PERCENTIL.INK', 'PERCENTILE.EXC': 'PERCENTIL.EXK', diff --git a/src/i18n/languages/trTR.ts b/src/i18n/languages/trTR.ts index 93c2176d37..d3f3806b2a 100644 --- a/src/i18n/languages/trTR.ts +++ b/src/i18n/languages/trTR.ts @@ -387,6 +387,8 @@ const dictionary: RawTranslationPackage = { IMTAN: 'SANTAN', LARGE: 'BÜYÜK', SMALL: 'KÜÇÜK', + 'RANK.EQ': 'RANK.EQ', + RANK: 'RANK', PERCENTILE: 'YÜZDEBİRLİK', 'PERCENTILE.INC': 'YÜZDEBİRLİK.DHL', 'PERCENTILE.EXC': 'YÜZDEBİRLİK.HRC', diff --git a/src/interpreter/functionMetadata/categories/statistical.ts b/src/interpreter/functionMetadata/categories/statistical.ts index 1752c3eaf6..25808b4a10 100644 --- a/src/interpreter/functionMetadata/categories/statistical.ts +++ b/src/interpreter/functionMetadata/categories/statistical.ts @@ -444,6 +444,13 @@ export const STATISTICAL_DOCS: Record = { documentationUrl: 'https://hyperformula.handsontable.com/docs/guide/built-in-functions.html', examples: ['=QUARTILE.INC(A1:A10, 3)'], }, + 'RANK.EQ': { + category: 'Statistical', + shortDescription: 'Returns the rank of a number in a range; tied numbers share the top rank.', + parameters: [{name: 'number', description: 'The number whose rank to find.'}, {name: 'ref', description: 'The range of numbers to rank against; non-numeric cells are ignored.'}, {name: 'order', description: 'Optional. 0 or omitted ranks in descending order; any other value ranks in ascending order.'}], + documentationUrl: 'https://hyperformula.handsontable.com/docs/guide/built-in-functions.html', + examples: ['=RANK.EQ(A1, A1:A10)', '=RANK.EQ(A1, A1:A10, 1)'], + }, RSQ: { category: 'Statistical', shortDescription: 'Returns the squared correlation coefficient between two data sets.', diff --git a/src/interpreter/plugin/MedianPlugin.ts b/src/interpreter/plugin/MedianPlugin.ts index a6a2a1078e..3825e46374 100644 --- a/src/interpreter/plugin/MedianPlugin.ts +++ b/src/interpreter/plugin/MedianPlugin.ts @@ -38,6 +38,18 @@ export class MedianPlugin extends FunctionPlugin implements FunctionPluginTypech {argumentType: FunctionArgumentType.NUMBER, minValue: 1}, ], }, + 'RANK.EQ': { + method: 'rankEq', + parameters: [ + {argumentType: FunctionArgumentType.NUMBER}, + {argumentType: FunctionArgumentType.RANGE}, + {argumentType: FunctionArgumentType.NUMBER, defaultValue: 0}, + ], + }, + } + + public static aliases = { + RANK: 'RANK.EQ', } /** @@ -100,4 +112,30 @@ export class MedianPlugin extends FunctionPlugin implements FunctionPluginTypech } ) } + + /** + * Corresponds to RANK.EQ(number, ref, [order]) and RANK(number, ref, [order]). + * + * Returns the rank of a number within a range; tied numbers share the top rank of the group. + * Order 0 (default) ranks descending, any other value ranks ascending. Only numeric cells of `ref` are considered. + * + * @param ast - procedure AST node + * @param state - interpreter state + */ + public rankEq(ast: ProcedureAst, state: InterpreterState): InterpreterValue { + return this.runFunction(ast.args, state, this.metadata('RANK.EQ'), + (target: number, range: SimpleRangeValue, order: number) => { + const numbers = this.arithmeticHelper.manyToExactNumbers(range.valuesFromTopLeftCorner()) + if (numbers instanceof CellError) { + return numbers + } + if (!numbers.includes(target)) { + return new CellError(ErrorType.NA, ErrorMessage.ValueNotFound) + } + const isAscending = order !== 0 + const better = numbers.filter(value => isAscending ? value < target : value > target) + return better.length + 1 + } + ) + } } diff --git a/src/license/functionCapabilities.ts b/src/license/functionCapabilities.ts index a26733ec5f..7352d00434 100644 --- a/src/license/functionCapabilities.ts +++ b/src/license/functionCapabilities.ts @@ -92,7 +92,7 @@ const UNGROUPED_FUNCTIONS = [ 'ISREF', 'LCM', 'LOG10', 'LOGNORM.DIST', 'LOGNORM.INV', 'MAXA', 'MAXPOOL', 'MEDIANPOOL', 'MINA', 'MIRR', 'MMULT', 'MULTINOMIAL', 'NEGBINOM.DIST', 'NETWORKDAYS.INTL', 'NOMINAL', 'NORM.DIST', 'NORM.INV', 'NORM.S.DIST', 'NORM.S.INV', 'NPER', 'OCT2BIN', 'OCT2DEC', 'OCT2HEX', 'PDURATION', 'PERCENTILE.EXC', - 'PHI', 'POISSON.DIST', 'QUARTILE.EXC', 'QUARTILE.INC', 'RADIANS', 'ROMAN', 'RRI', 'RSQ', 'SEC', 'SECH', + 'PHI', 'POISSON.DIST', 'QUARTILE.EXC', 'QUARTILE.INC', 'RADIANS', 'RANK', 'RANK.EQ', 'ROMAN', 'RRI', 'RSQ', 'SEC', 'SECH', 'SERIESSUM', 'SHEET', 'SHEETS', 'SINH', 'SKEW', 'SKEW.P', 'SLOPE', 'SPLIT', 'SQRTPI', 'STANDARDIZE', 'STEYX', 'SUMX2MY2', 'SUMX2PY2', 'SYD', 'T.DIST', 'T.DIST.2T', 'T.DIST.RT', 'T.INV', 'T.INV.2T', 'T.TEST', 'TANH', 'TBILLEQ', 'TBILLPRICE', 'TBILLYIELD', 'TDIST', 'TIMEVALUE', 'UNICODE', 'VARA', 'VARPA',