From ce83871f2b8903472541ef7095bc80e6222e7fcb Mon Sep 17 00:00:00 2001 From: nihilist-narwhal <240798405+nihilist-narwhal@users.noreply.github.com> Date: Sat, 10 Oct 2026 03:40:56 -0500 Subject: [PATCH] Remove Duplicates: compare the text cells show Excel decides duplicates by what a cell displays, not the value stored: the same date shown as 3/8/2006 and as Mar 8, 2006 is two values, while 1.01 and 1.02 both shown as 1.0 are duplicates. Remove Duplicates compared stored values, so it did the opposite in both cases. --- crates/engine/src/cmd/data.rs | 5 ++++- crates/engine/src/tests.rs | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/crates/engine/src/cmd/data.rs b/crates/engine/src/cmd/data.rs index 94733071c..7372380a4 100644 --- a/crates/engine/src/cmd/data.rs +++ b/crates/engine/src/cmd/data.rs @@ -691,8 +691,11 @@ fn remove_duplicates(s: &mut Session, p: &Json) -> Result { let Some(sh) = cx.wb.sheet(sheet) else { return Ok(()) }; let mut seen = std::collections::HashSet::new(); let mut keep: Vec = Vec::new(); + // Cells are compared by the text they show, as in Excel: the same date in two formats + // isn't a duplicate, while 1.01 and 1.02 shown as 1.0 are. + let shown = |c: CellRef| crate::display::format(&sh.value(c), cx.wb.styles.get(sh.style_id(c)).num_fmt.as_str(), &cx.wb).text; for row in start..=r.end.row { - let key: Vec = cols.iter().map(|c| sh.value(CellRef::new(row, *c)).display().to_lowercase()).collect(); + let key: Vec = cols.iter().map(|c| shown(CellRef::new(row, *c)).to_lowercase()).collect(); if seen.insert(key) { keep.push(row); } else { diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index 92ca38665..139a24985 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -615,6 +615,18 @@ fn case_sensitive_sort_puts_lowercase_first() { assert_eq!(col, ["a", "A", "aB", "Ab", "b", "B"].map(Value::from)); } +#[test] +fn remove_duplicates_compares_shown_text() { + let mut s = s(); + s.execute("range.setValues", json!({"range": "A1", "values": [["Date", "Price"], ["2006-03-08", 1.01], ["2006-03-08", 1.02]]})).unwrap(); + s.execute("home.numberFormat", json!({"range": "A3", "code": "mmm d, yyyy"})).unwrap(); + s.execute("home.numberFormat", json!({"range": "B2:B3", "code": "0.0"})).unwrap(); + let r = s.execute("data.removeDuplicates", json!({"range": "A1:A3", "header": true})).unwrap(); + assert_eq!(r["removed"], 0); + let r = s.execute("data.removeDuplicates", json!({"range": "B1:B3", "header": true})).unwrap(); + assert_eq!(r["removed"], 1); +} + #[test] fn formatting_and_styles() { let mut s = s();