From 4c2bcd637c97a6b065404edf44b4b042d2315cd9 Mon Sep 17 00:00:00 2001 From: neilcawse <8085959+neilcawse@users.noreply.github.com> Date: Mon, 12 Oct 2026 14:34:18 -0400 Subject: [PATCH] Functions: *IFS with two or more criteria pairs match the first 255 characters of a longer criterion, as in Excel --- crates/functions/src/criteria.rs | 16 +++++++++++++++- crates/functions/src/math.rs | 21 ++++++++++++--------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/crates/functions/src/criteria.rs b/crates/functions/src/criteria.rs index 2c87a9a..aa57177 100644 --- a/crates/functions/src/criteria.rs +++ b/crates/functions/src/criteria.rs @@ -53,7 +53,6 @@ impl Criterion { /// Parses the criteria argument of COUNTIF, SUMIF, AVERAGEIF and the single-pair *IFS /// functions: like `parse`, but text longer than 255 characters is `#VALUE!`, as in Excel. - /// The *IFS functions with two or more criteria pairs use `parse`: Excel doesn't limit them. pub(crate) fn parse_arg(v: &Value) -> Result { match v { Value::Array(a) => Criterion::parse_arg(a.data.first().unwrap_or(&Value::Empty)), @@ -62,6 +61,21 @@ impl Criterion { } } + /// Parses a criteria argument of an *IFS function with two or more criteria pairs. Excel + /// doesn't reject a longer text criterion there: it matches its first 255 characters + /// (checked on Excel 2016 and Microsoft 365 with COUNTIFS, SUMIFS, AVERAGEIFS, MAXIFS and + /// MINIFS). + pub(crate) fn parse_arg_truncated(v: &Value) -> Criterion { + match v { + Value::Array(a) => Criterion::parse_arg_truncated(a.data.first().unwrap_or(&Value::Empty)), + Value::Text(t) => match t.char_indices().nth(MAX_CRITERION_CHARS) { + Some((end, _)) => Criterion::parse_text(&t[..end]), + None => Criterion::parse_text(t), + }, + other => Criterion::parse(other), + } + } + fn parse_text(t: &str) -> Criterion { let (op, rest) = if let Some(r) = t.strip_prefix(">=") { (Op::Ge, r) diff --git a/crates/functions/src/math.rs b/crates/functions/src/math.rs index 75cea93..8c67aa8 100644 --- a/crates/functions/src/math.rs +++ b/crates/functions/src/math.rs @@ -950,10 +950,9 @@ fn criteria_mask(a: &[Arg], first_pair: usize, rows: usize, cols: usize) -> R R0")]), 1.0); - close(ev("COUNTIFS", vec![rf(qty.clone()), t(">0"), rf(cells.clone()), t(&long)]), 1.0); - close(ev("SUMIFS", vec![rf(qty.clone()), rf(cells.clone()), t(&long), rf(qty), t(">0")]), 2.0); + close(ev("SUMIFS", vec![rf(qty.clone()), rf(cells.clone()), t(&long), rf(qty.clone()), t(">0")]), 1.0); + close(ev("SUMIFS", vec![rf(qty.clone()), rf(qty.clone()), t(">0"), rf(cells.clone()), t(&over)]), 1.0); + close(ev("AVERAGEIFS", vec![rf(qty.clone()), rf(cells.clone()), t(&long), rf(qty.clone()), t(">0")]), 1.0); + close(ev("MAXIFS", vec![rf(qty.clone()), rf(cells.clone()), t(&"x".repeat(300)), rf(qty), t(">0")]), 1.0); } }