From c1568ad2ba283336378e135ce329bb4c4c1b92f2 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 25 Mar 2024 18:18:48 +0200 Subject: [PATCH] fix massive issue in getAllPossibleCombinations --- src/services/theme_data/iss_utils.js | 16 +++++++++++++--- .../services/theme_data/theme_data3.spec.js | 18 ++++++++++++++++-- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/services/theme_data/iss_utils.js b/src/services/theme_data/iss_utils.js index 6568f5760f..2d9dd0b59b 100644 --- a/src/services/theme_data/iss_utils.js +++ b/src/services/theme_data/iss_utils.js @@ -11,18 +11,28 @@ export const unroll = (item) => { } // This gives you an array of arrays of all possible unique (i.e. order-insensitive) combinations +// Can only accept primitives. Duplicates are not supported and can cause unexpected behavior export const getAllPossibleCombinations = (array) => { const combos = [array.map(x => [x])] for (let comboSize = 2; comboSize <= array.length; comboSize++) { const previous = combos[combos.length - 1] - const selfSet = new Set() const newCombos = previous.map(self => { + const selfSet = new Set() self.forEach(x => selfSet.add(x)) const nonSelf = array.filter(x => !selfSet.has(x)) return nonSelf.map(x => [...self, x]) }) const flatCombos = newCombos.reduce((acc, x) => [...acc, ...x], []) - combos.push(flatCombos) + const uniqueComboStrings = new Set() + const uniqueCombos = flatCombos.map(x => x.toSorted()).filter(x => { + if (uniqueComboStrings.has(x.join())) { + return false + } else { + uniqueComboStrings.add(x.join()) + return true + } + }) + combos.push(uniqueCombos) } return combos.reduce((acc, x) => [...acc, ...x], []) } @@ -31,7 +41,7 @@ export const getAllPossibleCombinations = (array) => { export const genericRuleToSelector = components => (rule, ignoreOutOfTreeSelector, isParent) => { if (!rule && !isParent) return null const component = components[rule.component] - const { states, variants, selector, outOfTreeSelector } = component + const { states = {}, variants = {}, selector, outOfTreeSelector } = component const applicableStates = ((rule.state || []).filter(x => x !== 'normal')).map(state => states[state]) diff --git a/test/unit/specs/services/theme_data/theme_data3.spec.js b/test/unit/specs/services/theme_data/theme_data3.spec.js index 25a9dda4d1..37d343f9fe 100644 --- a/test/unit/specs/services/theme_data/theme_data3.spec.js +++ b/test/unit/specs/services/theme_data/theme_data3.spec.js @@ -11,9 +11,23 @@ import { describe.only('Theme Data 3', () => { describe('getAllPossibleCombinations', () => { - it('test simple case', () => { + it('test simple 3 values case', () => { const out = getAllPossibleCombinations([1, 2, 3]).map(x => x.sort((a, b) => a - b)) - expect(out).to.eql([[1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]) + expect(out).to.eql([ + [1], [2], [3], + [1, 2], [1, 3], [2, 3], + [1, 2, 3] + ]) + }) + + it('test simple 4 values case', () => { + const out = getAllPossibleCombinations([1, 2, 3, 4]).map(x => x.sort((a, b) => a - b)) + expect(out).to.eql([ + [1], [2], [3], [4], + [1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4], + [1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4], + [1, 2, 3, 4] + ]) }) })