From 3f06064e7cc13984a7ae3ba17f39f930be97cb19 Mon Sep 17 00:00:00 2001 From: tobiadefami Date: Thu, 6 Aug 2026 21:56:47 +0100 Subject: [PATCH 1/4] feat: implement AVERAGEIFS function --- .../plugin/ConditionalAggregationPlugin.ts | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/interpreter/plugin/ConditionalAggregationPlugin.ts b/src/interpreter/plugin/ConditionalAggregationPlugin.ts index f2e74e83cf..9c7700f64b 100644 --- a/src/interpreter/plugin/ConditionalAggregationPlugin.ts +++ b/src/interpreter/plugin/ConditionalAggregationPlugin.ts @@ -99,6 +99,15 @@ export class ConditionalAggregationPlugin extends FunctionPlugin implements Func {argumentType: FunctionArgumentType.RANGE, optionalArg: true}, ], }, + AVERAGEIFS: { + method: 'averageifs', + parameters: [ + {argumentType: FunctionArgumentType.RANGE}, + {argumentType: FunctionArgumentType.RANGE}, + {argumentType: FunctionArgumentType.NOERROR}, + ], + repeatLastArgs: 2, + }, SUMIFS: { method: 'sumifs', parameters: [ @@ -207,6 +216,56 @@ export class ConditionalAggregationPlugin extends FunctionPlugin implements Func return this.runFunction(ast.args, state, this.metadata(functionName), computeFn) } + /** + * Corresponds to AVERAGEIFS(AverageRange, CriterionRange1, Criterion1, ...) + * + * AverageRange is the range whose matching numeric values are averaged. + * Every CriterionRange and Criterion pair adds a condition that must be satisfied. + * + * @param ast + * @param state + */ + public averageifs(ast: ProcedureAst, state: InterpreterState): InterpreterValue { + const functionName = 'AVERAGEIFS' + + const composeFunction = (left: AverageResult | CellError, right: AverageResult | CellError): AverageResult | CellError => { + if (left instanceof CellError) { + return left + } else if (right instanceof CellError) { + return right + } else { + return left.compose(right) + } + } + + const computeFn = (values: SimpleRangeValue, ...args: unknown[]) => { + const averageResult = this.computeConditionalAggregationFunction( + values, + args as RawInterpreterValue[], + functionName, + AverageResult.empty, + composeFunction, + (arg) => { + if (arg instanceof CellError) { + return arg + } else if (isExtendedNumber(arg)) { + return AverageResult.single(getRawValue(arg)) + } else { + return AverageResult.empty + } + }, + ) + + if (averageResult instanceof CellError) { + return averageResult + } else { + return averageResult.averageValue() ?? new CellError(ErrorType.DIV_BY_ZERO) + } + } + + return this.runFunction(ast.args, state, this.metadata(functionName), computeFn) + } + /** * Corresponds to COUNTIF(Range, Criterion) * From 0aca5ab789da7730c6f8a82379321dc9a02125fb Mon Sep 17 00:00:00 2001 From: tobiadefami Date: Thu, 6 Aug 2026 21:57:05 +0100 Subject: [PATCH 2/4] docs: describe AVERAGEIFS function --- CHANGELOG.md | 4 ++++ src/i18n/languages/csCZ.ts | 1 + src/i18n/languages/daDK.ts | 1 + src/i18n/languages/deDE.ts | 1 + src/i18n/languages/enGB.ts | 1 + src/i18n/languages/esES.ts | 1 + src/i18n/languages/fiFI.ts | 1 + src/i18n/languages/frFR.ts | 1 + src/i18n/languages/huHU.ts | 1 + src/i18n/languages/idID.ts | 1 + src/i18n/languages/itIT.ts | 1 + src/i18n/languages/nbNO.ts | 1 + src/i18n/languages/nlNL.ts | 1 + src/i18n/languages/plPL.ts | 1 + src/i18n/languages/ptPT.ts | 1 + src/i18n/languages/ruRU.ts | 1 + src/i18n/languages/svSE.ts | 1 + src/i18n/languages/trTR.ts | 1 + src/interpreter/functionMetadata/categories/statistical.ts | 7 +++++++ 19 files changed, 28 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24ecffc1c4..be9271091d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Added + +- Added the `AVERAGEIFS` function. + ## [3.4.0] - 2026-08-10 ### Added diff --git a/src/i18n/languages/csCZ.ts b/src/i18n/languages/csCZ.ts index 830b1c1968..f7bb42ca38 100644 --- a/src/i18n/languages/csCZ.ts +++ b/src/i18n/languages/csCZ.ts @@ -38,6 +38,7 @@ const dictionary: RawTranslationPackage = { AVERAGE: 'PRŮMĚR', AVERAGEA: 'AVERAGEA', AVERAGEIF: 'AVERAGEIF', + AVERAGEIFS: 'AVERAGEIFS', BASE: 'BASE', BIN2DEC: 'BIN2DEC', BIN2HEX: 'BIN2HEX', diff --git a/src/i18n/languages/daDK.ts b/src/i18n/languages/daDK.ts index 90296fd420..a86e58d876 100644 --- a/src/i18n/languages/daDK.ts +++ b/src/i18n/languages/daDK.ts @@ -38,6 +38,7 @@ const dictionary: RawTranslationPackage = { AVERAGE: 'MIDDEL', AVERAGEA: 'MIDDELV', AVERAGEIF: 'MIDDEL.HVIS', + AVERAGEIFS: 'MIDDEL.HVISER', BASE: 'BASIS', BIN2DEC: 'BIN.TIL.DEC', BIN2HEX: 'BIN.TIL.HEX', diff --git a/src/i18n/languages/deDE.ts b/src/i18n/languages/deDE.ts index daaaa0bf70..bcf011df41 100644 --- a/src/i18n/languages/deDE.ts +++ b/src/i18n/languages/deDE.ts @@ -38,6 +38,7 @@ const dictionary: RawTranslationPackage = { AVERAGE: 'MITTELWERT', AVERAGEA: 'MITTELWERTA', AVERAGEIF: 'MITTELWERTWENN', + AVERAGEIFS: 'MITTELWERTWENNS', BASE: 'BASIS', BIN2DEC: 'BININDEZ', BIN2HEX: 'BININHEX', diff --git a/src/i18n/languages/enGB.ts b/src/i18n/languages/enGB.ts index d271b732cd..2eaa4bd9cc 100644 --- a/src/i18n/languages/enGB.ts +++ b/src/i18n/languages/enGB.ts @@ -39,6 +39,7 @@ const dictionary: RawTranslationPackage = { AVERAGE: 'AVERAGE', AVERAGEA: 'AVERAGEA', AVERAGEIF: 'AVERAGEIF', + AVERAGEIFS: 'AVERAGEIFS', BASE: 'BASE', BIN2DEC: 'BIN2DEC', BIN2HEX: 'BIN2HEX', diff --git a/src/i18n/languages/esES.ts b/src/i18n/languages/esES.ts index 6fea52e4e2..687c4ea945 100644 --- a/src/i18n/languages/esES.ts +++ b/src/i18n/languages/esES.ts @@ -38,6 +38,7 @@ export const dictionary: RawTranslationPackage = { AVERAGE: 'PROMEDIO', AVERAGEA: 'PROMEDIOA', AVERAGEIF: 'PROMEDIO.SI', + AVERAGEIFS: 'PROMEDIO.SI.CONJUNTO', BASE: 'BASE', BIN2DEC: 'BIN.A.DEC', BIN2HEX: 'BIN.A.HEX', diff --git a/src/i18n/languages/fiFI.ts b/src/i18n/languages/fiFI.ts index a3b319af2a..96c162ef22 100644 --- a/src/i18n/languages/fiFI.ts +++ b/src/i18n/languages/fiFI.ts @@ -38,6 +38,7 @@ const dictionary: RawTranslationPackage = { AVERAGE: 'KESKIARVO', AVERAGEA: 'KESKIARVOA', AVERAGEIF: 'KESKIARVO.JOS', + AVERAGEIFS: 'KESKIARVO.JOS.JOUKKO', BASE: 'PERUS', BIN2DEC: 'BINDES', BIN2HEX: 'BINHEKSA', diff --git a/src/i18n/languages/frFR.ts b/src/i18n/languages/frFR.ts index a29109053c..4d0839a168 100644 --- a/src/i18n/languages/frFR.ts +++ b/src/i18n/languages/frFR.ts @@ -38,6 +38,7 @@ const dictionary: RawTranslationPackage = { AVERAGE: 'MOYENNE', AVERAGEA: 'AVERAGEA', AVERAGEIF: 'MOYENNE.SI', + AVERAGEIFS: 'MOYENNE.SI.ENS', BASE: 'BASE', BIN2DEC: 'BINDEC', BIN2HEX: 'BINHEX', diff --git a/src/i18n/languages/huHU.ts b/src/i18n/languages/huHU.ts index fafc2c4b9b..cb803b4c94 100644 --- a/src/i18n/languages/huHU.ts +++ b/src/i18n/languages/huHU.ts @@ -38,6 +38,7 @@ const dictionary: RawTranslationPackage = { AVERAGE: 'ÁTLAG', AVERAGEA: 'ÁTLAGA', AVERAGEIF: 'ÁTLAGHA', + AVERAGEIFS: 'ÁTLAGHATÖBB', BASE: 'ALAP', BIN2DEC: 'BIN.DEC', BIN2HEX: 'BIN.HEX', diff --git a/src/i18n/languages/idID.ts b/src/i18n/languages/idID.ts index 719cb11db6..4e52987d10 100644 --- a/src/i18n/languages/idID.ts +++ b/src/i18n/languages/idID.ts @@ -39,6 +39,7 @@ const dictionary: RawTranslationPackage = { AVERAGE: 'RATA-RATA', AVERAGEA: 'RATA-RATAA', AVERAGEIF: 'RATA-RATA.JIKA', + AVERAGEIFS: 'AVERAGEIFS', BASE: 'BASIS', BIN2DEC: 'BIN.DEC', BIN2HEX: 'BIN.HEX', diff --git a/src/i18n/languages/itIT.ts b/src/i18n/languages/itIT.ts index 40ec9cf414..ee92314506 100644 --- a/src/i18n/languages/itIT.ts +++ b/src/i18n/languages/itIT.ts @@ -38,6 +38,7 @@ const dictionary: RawTranslationPackage = { AVERAGE: 'MEDIA', AVERAGEA: 'MEDIA.VALORI', AVERAGEIF: 'MEDIA.SE', + AVERAGEIFS: 'MEDIA.PIÙ.SE', BASE: 'BASE', BIN2DEC: 'BINARIO.DECIMALE', BIN2HEX: 'BINARIO.HEX', diff --git a/src/i18n/languages/nbNO.ts b/src/i18n/languages/nbNO.ts index 89c0f85f76..5878a9f425 100644 --- a/src/i18n/languages/nbNO.ts +++ b/src/i18n/languages/nbNO.ts @@ -38,6 +38,7 @@ const dictionary: RawTranslationPackage = { AVERAGE: 'GJENNOMSNITT', AVERAGEA: 'GJENNOMSNITTA', AVERAGEIF: 'GJENNOMSNITTHVIS', + AVERAGEIFS: 'GJENNOMSNITT.HVIS.SETT', BASE: 'BASE', BIN2DEC: 'BINTILDES', BIN2HEX: 'BINTILHEKS', diff --git a/src/i18n/languages/nlNL.ts b/src/i18n/languages/nlNL.ts index 73ef99687b..2c395cd3d0 100644 --- a/src/i18n/languages/nlNL.ts +++ b/src/i18n/languages/nlNL.ts @@ -38,6 +38,7 @@ const dictionary: RawTranslationPackage = { AVERAGE: 'GEMIDDELDE', AVERAGEA: 'GEMIDDELDEA', AVERAGEIF: 'GEMIDDELDE.ALS', + AVERAGEIFS: 'GEMIDDELDEN.ALS', BASE: 'BASIS', BIN2DEC: 'BIN.N.DEC', BIN2HEX: 'BIN.N.HEX', diff --git a/src/i18n/languages/plPL.ts b/src/i18n/languages/plPL.ts index 003b0fd648..8268ac46c3 100644 --- a/src/i18n/languages/plPL.ts +++ b/src/i18n/languages/plPL.ts @@ -38,6 +38,7 @@ const dictionary: RawTranslationPackage = { AVERAGE: 'ŚREDNIA', AVERAGEA: 'ŚREDNIA.A', AVERAGEIF: 'ŚREDNIA.JEŻELI', + AVERAGEIFS: 'ŚREDNIA.WARUNKÓW', BASE: 'PODSTAWA', BIN2DEC: 'DWÓJK.NA.DZIES', BIN2HEX: 'DWÓJK.NA.SZESN', diff --git a/src/i18n/languages/ptPT.ts b/src/i18n/languages/ptPT.ts index 408d7f7722..8ac3aaa45a 100644 --- a/src/i18n/languages/ptPT.ts +++ b/src/i18n/languages/ptPT.ts @@ -38,6 +38,7 @@ const dictionary: RawTranslationPackage = { AVERAGE: 'MÉDIA', AVERAGEA: 'MÉDIAA', AVERAGEIF: 'MÉDIASE', + AVERAGEIFS: 'MÉDIASES', BASE: 'BASE', BIN2DEC: 'BINADEC', BIN2HEX: 'BINAHEX', diff --git a/src/i18n/languages/ruRU.ts b/src/i18n/languages/ruRU.ts index 657b0db373..7e120546a3 100644 --- a/src/i18n/languages/ruRU.ts +++ b/src/i18n/languages/ruRU.ts @@ -38,6 +38,7 @@ const dictionary: RawTranslationPackage = { AVERAGE: 'СРЗНАЧ', AVERAGEA: 'СРЗНАЧА', AVERAGEIF: 'СРЗНАЧЕСЛИ', + AVERAGEIFS: 'СРЗНАЧЕСЛИМН', BASE: 'ОСНОВАНИЕ', BIN2DEC: 'ДВ.В.ДЕС', BIN2HEX: 'ДВ.В.ШЕСТН', diff --git a/src/i18n/languages/svSE.ts b/src/i18n/languages/svSE.ts index 5f87580087..fe5101ad2f 100644 --- a/src/i18n/languages/svSE.ts +++ b/src/i18n/languages/svSE.ts @@ -38,6 +38,7 @@ const dictionary: RawTranslationPackage = { AVERAGE: 'MEDEL', AVERAGEA: 'AVERAGEA', AVERAGEIF: 'MEDEL.OM', + AVERAGEIFS: 'MEDEL.OMF', BASE: 'BASE', BIN2DEC: 'BIN.TILL.DEC', BIN2HEX: 'BIN.TILL.HEX', diff --git a/src/i18n/languages/trTR.ts b/src/i18n/languages/trTR.ts index dc243600d5..76c85418d3 100644 --- a/src/i18n/languages/trTR.ts +++ b/src/i18n/languages/trTR.ts @@ -38,6 +38,7 @@ const dictionary: RawTranslationPackage = { AVERAGE: 'ORTALAMA', AVERAGEA: 'ORTALAMAA', AVERAGEIF: 'EĞERORTALAMA', + AVERAGEIFS: 'ÇOKEĞERORTALAMA', BASE: 'TABAN', BIN2DEC: 'BIN2DEC', BIN2HEX: 'BIN2HEX', diff --git a/src/interpreter/functionMetadata/categories/statistical.ts b/src/interpreter/functionMetadata/categories/statistical.ts index 1752c3eaf6..47dc48d224 100644 --- a/src/interpreter/functionMetadata/categories/statistical.ts +++ b/src/interpreter/functionMetadata/categories/statistical.ts @@ -38,6 +38,13 @@ export const STATISTICAL_DOCS: Record = { documentationUrl: 'https://hyperformula.handsontable.com/docs/guide/built-in-functions.html', examples: ['=AVERAGEIF(A1:A10, ">5")', '=AVERAGEIF(B1:B10, "apples", C1:C10)'], }, + AVERAGEIFS: { + category: 'Statistical', + shortDescription: 'Returns the arithmetic mean of the cells in a range that meet a set of criteria.', + parameters: [{name: 'average_range', description: 'The range of cells to average.'}, {name: 'criteria_range1', description: 'A range of cells tested against the paired criterion. Further range/criterion pairs can be passed as additional arguments, and only cells meeting every criterion are considered.'}, {name: 'criteria1', description: 'The condition applied to the preceding range, e.g. ">5", "apples", or a cell reference.'}], + documentationUrl: 'https://hyperformula.handsontable.com/docs/guide/built-in-functions.html', + examples: ['=AVERAGEIFS(C1:C10, A1:A10, ">5")', '=AVERAGEIFS(C1:C10, A1:A10, ">5", B1:B10, "apples")'], + }, 'BETA.DIST': { category: 'Statistical', shortDescription: 'Returns the density of the beta distribution.', From 46ac5744ee56bb5de9e44b082b2638ed2bbd58a5 Mon Sep 17 00:00:00 2001 From: tobiadefami Date: Wed, 12 Aug 2026 21:18:16 +0100 Subject: [PATCH 3/4] fix: correct Portuguese AVERAGEIFS translation --- src/i18n/languages/ptPT.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/languages/ptPT.ts b/src/i18n/languages/ptPT.ts index 8ac3aaa45a..a95a904dba 100644 --- a/src/i18n/languages/ptPT.ts +++ b/src/i18n/languages/ptPT.ts @@ -38,7 +38,7 @@ const dictionary: RawTranslationPackage = { AVERAGE: 'MÉDIA', AVERAGEA: 'MÉDIAA', AVERAGEIF: 'MÉDIASE', - AVERAGEIFS: 'MÉDIASES', + AVERAGEIFS: 'MÉDIA.SE.S', BASE: 'BASE', BIN2DEC: 'BINADEC', BIN2HEX: 'BINAHEX', From 8f54b3ab0fc5aeee87422b326919b50b35d652cc Mon Sep 17 00:00:00 2001 From: tobiadefami Date: Thu, 13 Aug 2026 10:09:15 +0100 Subject: [PATCH 4/4] docs: link AVERAGEIFS changelog entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be9271091d..e2d8caba52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Added -- Added the `AVERAGEIFS` function. +- Added the `AVERAGEIFS` function. [#1732](https://github.com/handsontable/hyperformula/pull/1732) ## [3.4.0] - 2026-08-10