From 5a2eeed06680080e0a29c31ce3740740b840f0fe Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Sat, 20 Jun 2026 09:52:56 +0200 Subject: [PATCH] fix Set Difference and Set Intersection preserve duplicates from first sample (#2286) --- src/core/operations/SetDifference.mjs | 9 +++++++- src/core/operations/SetIntersection.mjs | 9 +++++++- tests/operations/tests/SetDifference.mjs | 22 ++++++++++++++++++++ tests/operations/tests/SetIntersection.mjs | 24 +++++++++++++++++++++- 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/src/core/operations/SetDifference.mjs b/src/core/operations/SetDifference.mjs index dc46c079..d5ab92d3 100644 --- a/src/core/operations/SetDifference.mjs +++ b/src/core/operations/SetDifference.mjs @@ -75,9 +75,16 @@ class SetDifference extends Operation { * @returns {Object[]} */ runSetDifference(a, b) { + const excluded = new Set(b); + const seen = new Set(); + return a .filter((item) => { - return b.indexOf(item) === -1; + if (excluded.has(item) || seen.has(item)) { + return false; + } + seen.add(item); + return true; }) .join(this.itemDelimiter); } diff --git a/src/core/operations/SetIntersection.mjs b/src/core/operations/SetIntersection.mjs index 7e6dbe10..423fcd4f 100644 --- a/src/core/operations/SetIntersection.mjs +++ b/src/core/operations/SetIntersection.mjs @@ -75,9 +75,16 @@ class SetIntersection extends Operation { * @returns {Object[]} */ runIntersect(a, b) { + const included = new Set(b); + const seen = new Set(); + return a .filter((item) => { - return b.indexOf(item) > -1; + if (!included.has(item) || seen.has(item)) { + return false; + } + seen.add(item); + return true; }) .join(this.itemDelimiter); } diff --git a/tests/operations/tests/SetDifference.mjs b/tests/operations/tests/SetDifference.mjs index 40fac524..5836cb36 100644 --- a/tests/operations/tests/SetDifference.mjs +++ b/tests/operations/tests/SetDifference.mjs @@ -53,4 +53,26 @@ TestRegister.addTests([ }, ], }, + { + name: "Set Difference: duplicates in first set are removed", + input: "red,red,blue\n\nblue", + expectedOutput: "red", + recipeConfig: [ + { + op: "Set Difference", + args: ["\n\n", ","], + }, + ], + }, + { + name: "Set Difference: duplicates in both sets", + input: "1 1 2 2 3\n\n2 2 3 3", + expectedOutput: "1", + recipeConfig: [ + { + op: "Set Difference", + args: ["\n\n", " "], + }, + ], + }, ]); diff --git a/tests/operations/tests/SetIntersection.mjs b/tests/operations/tests/SetIntersection.mjs index c9146c01..a638db4a 100644 --- a/tests/operations/tests/SetIntersection.mjs +++ b/tests/operations/tests/SetIntersection.mjs @@ -52,5 +52,27 @@ TestRegister.addTests([ args: ["z", "-"], }, ], - } + }, + { + name: "Set Intersection: duplicates in first set are removed", + input: "red,red,blue\n\nred,blue", + expectedOutput: "red,blue", + recipeConfig: [ + { + op: "Set Intersection", + args: ["\n\n", ","], + }, + ], + }, + { + name: "Set Intersection: duplicates in both sets", + input: "1 1 2 2 3\n\n2 2 3 3 4", + expectedOutput: "2 3", + recipeConfig: [ + { + op: "Set Intersection", + args: ["\n\n", " "], + }, + ], + }, ]);