From c8308550dcdd8cbc6f6dc65fae4e5857d1416b81 Mon Sep 17 00:00:00 2001 From: Joshua Melville Date: Tue, 20 Jun 2023 14:31:55 +0200 Subject: [PATCH 1/4] Allow multiple values for exactly/not predicate This PR adds the ability to provide multiple values to the `exactly` and `not` predicates. This applies to categorical variables only. The comparison is done using the `isEqual` lodash method, with sorting applied to the values before comparison. This means that the order of the values provided to the predicate does not matter. Architect will need to be updated to produce rules that use this new functionality. --- __tests__/helpers.js | 26 ++ __tests__/predicate.test.js | 512 ++++++++++++++++++++++++++++-------- predicate.js | 26 +- 3 files changed, 440 insertions(+), 124 deletions(-) diff --git a/__tests__/helpers.js b/__tests__/helpers.js index df850e6..3c5bb81 100644 --- a/__tests__/helpers.js +++ b/__tests__/helpers.js @@ -24,5 +24,31 @@ const generateRuleConfig = (type, options) => ({ options, }); +// Function that checks if a string is a valid date in the format YYYY-MM-DD, YYYY-MM, or YYYY +// and converts it to a Date object +const isValidDate = (dateString) => { + const dateRegex = /^(\d{4})-(\d{2})-(\d{2})$/; + const yearMonthRegex = /^(\d{4})-(\d{2})$/; + const yearRegex = /^(\d{4})$/; + + if (dateRegex.test(dateString)) { + const [year, month, day] = dateString.split('-'); + return new Date(year, month - 1, day); + } + + if (yearMonthRegex.test(dateString)) { + const [year, month] = dateString.split('-'); + return new Date(year, month - 1); + } + + if (yearRegex.test(dateString)) { + const [year] = dateString.split('-'); + return new Date(year); + } + + return false; +}; + +exports.isValidDate = isValidDate; exports.getEntityGenerator = getEntityGenerator; exports.generateRuleConfig = generateRuleConfig; diff --git a/__tests__/predicate.test.js b/__tests__/predicate.test.js index a4feec0..855bbcb 100644 --- a/__tests__/predicate.test.js +++ b/__tests__/predicate.test.js @@ -3,142 +3,422 @@ const predicate = require('../predicate').default; const operators = require('../predicate').operators; const countOperators = require('../predicate').countOperators; +/** + * Should cover all variable types: + * - number + * - string + * - date + * - boolean + * - categorical + * - ordinal + * - scalar + */ + describe('predicate', () => { it('default', () => { expect(predicate(null)({ value: null, other: null })).toBe(false); }); describe('operators', () => { - it('GREATER_THAN', () => { - expect( - predicate(operators.GREATER_THAN)({ value: 1.5, other: 1 }), - ).toBe(true); - expect( - predicate(operators.GREATER_THAN)({ value: 2, other: 2 }), - ).toBe(false); + describe('GREATER_THAN', () => { + it('number', () => { + expect( + predicate(operators.GREATER_THAN)({ value: 1.5, other: 1 }), + ).toBe(true); + expect( + predicate(operators.GREATER_THAN)({ value: 2, other: 2 }), + ).toBe(false); + }); + it('date', () => { + expect( + predicate(operators.GREATER_THAN)({ value: '2018-01-01', other: '2017-01-01' }), + ).toBe(true); + expect( + predicate(operators.GREATER_THAN)({ value: '2018-01-01', other: '2018-01-01' }), + ).toBe(false); + }); + + it('scalar', () => { + expect( + predicate(operators.GREATER_THAN)({ value: 0.5, other: 0.3 }), + ).toBe(true); + expect( + predicate(operators.GREATER_THAN)({ value: 0.1, other: 0.2 }), + ).toBe(false); + }); }); - it('LESS_THAN', () => { - expect( - predicate(operators.LESS_THAN)({ value: 1, other: 1.5 }), - ).toBe(true); - expect( - predicate(operators.LESS_THAN)({ value: 2, other: 2 }), - ).toBe(false); + describe('LESS_THAN', () => { + it('number', () => { + expect( + predicate(operators.LESS_THAN)({ value: 1, other: 1.5 }), + ).toBe(true); + expect( + predicate(operators.LESS_THAN)({ value: 2, other: 2 }), + ).toBe(false); + }); + it('date', () => { + expect( + predicate(operators.LESS_THAN)({ value: '2017-01-01', other: '2018-01-01' }), + ).toBe(true); + expect( + predicate(operators.LESS_THAN)({ value: '2018-01-01', other: '2018-01-01' }), + ).toBe(false); + }); + + it('scalar', () => { + expect( + predicate(operators.LESS_THAN)({ value: 0.3, other: 0.5 }), + ).toBe(true); + expect( + predicate(operators.LESS_THAN)({ value: 0.2, other: 0.1 }), + ).toBe(false); + }); }); - it('GREATER_THAN_OR_EQUAL', () => { - expect( - predicate(operators.GREATER_THAN_OR_EQUAL)({ value: 1.5, other: 1 }), - ).toBe(true); - expect( - predicate(operators.GREATER_THAN_OR_EQUAL)({ value: 2, other: 2 }), - ).toBe(true); - expect( - predicate(operators.GREATER_THAN_OR_EQUAL)({ value: 2, other: 3 }), - ).toBe(false); + describe('GREATER_THAN_OR_EQUAL', () => { + it('number', () => { + expect( + predicate(operators.GREATER_THAN_OR_EQUAL)({ value: 1.5, other: 1 }), + ).toBe(true); + expect( + predicate(operators.GREATER_THAN_OR_EQUAL)({ value: 2, other: 2 }), + ).toBe(true); + expect( + predicate(operators.GREATER_THAN_OR_EQUAL)({ value: 2, other: 3 }), + ).toBe(false); + }); + it('date', () => { + expect( + predicate(operators.GREATER_THAN_OR_EQUAL)({ value: '2018-01-01', other: '2017-01-01' }), + ).toBe(true); + expect( + predicate(operators.GREATER_THAN_OR_EQUAL)({ value: '2018-01-01', other: '2018-01-01' }), + ).toBe(true); + expect( + predicate(operators.GREATER_THAN_OR_EQUAL)({ value: '2018-01-01', other: '2019-01-01' }), + ).toBe(false); + }); + + it('scalar', () => { + expect( + predicate(operators.GREATER_THAN_OR_EQUAL)({ value: 0.5, other: 0.3 }), + ).toBe(true); + expect( + predicate(operators.GREATER_THAN_OR_EQUAL)({ value: 0.2, other: 0.2 }), + ).toBe(true); + expect( + predicate(operators.GREATER_THAN_OR_EQUAL)({ value: 0.1, other: 0.2 }), + ).toBe(false); + }); }); - it('LESS_THAN_OR_EQUAL', () => { - expect( - predicate(operators.LESS_THAN_OR_EQUAL)({ value: 1, other: 1.5 }), - ).toBe(true); - expect( - predicate(operators.LESS_THAN_OR_EQUAL)({ value: 2, other: 2 }), - ).toBe(true); - expect( - predicate(operators.LESS_THAN_OR_EQUAL)({ value: 3, other: 2 }), - ).toBe(false); + describe('LESS_THAN_OR_EQUAL', () => { + it('number', () => { + expect( + predicate(operators.LESS_THAN_OR_EQUAL)({ value: 1, other: 1.5 }), + ).toBe(true); + expect( + predicate(operators.LESS_THAN_OR_EQUAL)({ value: 2, other: 2 }), + ).toBe(true); + expect( + predicate(operators.LESS_THAN_OR_EQUAL)({ value: 3, other: 2 }), + ).toBe(false); + }); + + it('date', () => { + expect( + predicate(operators.LESS_THAN_OR_EQUAL)({ value: '2017-01-01', other: '2018-01-01' }), + ).toBe(true); + expect( + predicate(operators.LESS_THAN_OR_EQUAL)({ value: '2018-01-01', other: '2018-01-01' }), + ).toBe(true); + expect( + predicate(operators.LESS_THAN_OR_EQUAL)({ value: '2019-01-01', other: '2018-01-01' }), + ).toBe(false); + }); + + it('scalar', () => { + expect( + predicate(operators.LESS_THAN_OR_EQUAL)({ value: 0.3, other: 0.5 }), + ).toBe(true); + expect( + predicate(operators.LESS_THAN_OR_EQUAL)({ value: 0.2, other: 0.2 }), + ).toBe(true); + expect( + predicate(operators.LESS_THAN_OR_EQUAL)({ value: 0.2, other: 0.1 }), + ).toBe(false); + }); }); - it('EXACTLY', () => { - expect( - predicate(operators.EXACTLY)({ value: 1, other: 1 }), - ).toBe(true); - expect( - predicate(operators.EXACTLY)({ value: 2, other: 1 }), - ).toBe(false); - expect( - predicate(operators.EXACTLY)({ value: null, other: 0 }), - ).toBe(false); - expect( - predicate(operators.EXACTLY)({ value: 'word', other: 'word' }), - ).toBe(true); - expect( - predicate(operators.EXACTLY)({ value: 'not word', other: 'word' }), - ).toBe(false); - expect( - predicate(operators.EXACTLY)({ value: null, other: 'word' }), - ).toBe(false); - expect( - predicate(operators.EXACTLY)({ value: true, other: true }), - ).toBe(true); - expect( - predicate(operators.EXACTLY)({ value: false, other: true }), - ).toBe(false); - expect( - predicate(operators.EXACTLY)({ value: null, other: true }), - ).toBe(false); - expect( - predicate(operators.EXACTLY)({ value: true, other: false }), - ).toBe(false); - expect( - predicate(operators.EXACTLY)({ value: false, other: false }), - ).toBe(true); - expect( - predicate(operators.EXACTLY)({ value: null, other: false }), - ).toBe(false); - expect( - predicate(operators.EXACTLY)({ value: false, other: null }), - ).toBe(false); + describe('EXACTLY', () => { + it('number', () => { + expect( + predicate(operators.EXACTLY)({ value: 1, other: 1 }), + ).toBe(true); + expect( + predicate(operators.EXACTLY)({ value: 2, other: 1 }), + ).toBe(false); + expect( + predicate(operators.EXACTLY)({ value: null, other: 0 }), + ).toBe(false); + }); + it('string', () => { + expect( + predicate(operators.EXACTLY)({ value: 'word', other: 'word' }), + ).toBe(true); + expect( + predicate(operators.EXACTLY)({ value: 'not word', other: 'word' }), + ).toBe(false); + expect( + predicate(operators.EXACTLY)({ value: null, other: 'word' }), + ).toBe(false); + }); + it('boolean', () => { + expect( + predicate(operators.EXACTLY)({ value: true, other: true }), + ).toBe(true); + expect( + predicate(operators.EXACTLY)({ value: false, other: true }), + ).toBe(false); + expect( + predicate(operators.EXACTLY)({ value: null, other: true }), + ).toBe(false); + expect( + predicate(operators.EXACTLY)({ value: true, other: false }), + ).toBe(false); + expect( + predicate(operators.EXACTLY)({ value: false, other: false }), + ).toBe(true); + expect( + predicate(operators.EXACTLY)({ value: null, other: false }), + ).toBe(false); + expect( + predicate(operators.EXACTLY)({ value: false, other: null }), + ).toBe(false); + }); + + it('categorical', () => { + expect( + predicate(operators.EXACTLY)({ value: ['f'], other: ['f'] }), + ).toBe(true); + + expect( + predicate(operators.EXACTLY)({ value: ['f'], other: ['f', 'm'] }), + ).toBe(false); + + // Order shouldn't matter + expect( + predicate(operators.EXACTLY)({ value: ['f', 'm'], other: ['f', 'm'] }), + ).toBe(true); + + expect( + predicate(operators.EXACTLY)({ value: ['m', 'f'], other: ['f', 'm'] }), + ).toBe(true); + + expect( + predicate(operators.EXACTLY)({ value: [1], other: [1] }), + ).toBe(true); + + expect( + predicate(operators.EXACTLY)({ value: [1], other: [1, 2] }), + ).toBe(false); + }); + + it('ordinal', () => { + expect( + predicate(operators.EXACTLY)({ value: 'f', other: 'f' }), + ).toBe(true); + + expect( + predicate(operators.EXACTLY)({ value: 'f', other: 'm' }), + ).toBe(false); + + expect( + predicate(operators.EXACTLY)({ value: 1, other: 1 }), + ).toBe(true); + + expect( + predicate(operators.EXACTLY)({ value: 1, other: 2 }), + ).toBe(false); + + expect( + predicate(operators.EXACTLY)({ value: true, other: true }), + ).toBe(true); + + expect( + predicate(operators.EXACTLY)({ value: true, other: false }), + ).toBe(false); + }); + + it('scalar', () => { + expect( + predicate(operators.EXACTLY)({ value: 1, other: 1 }), + ).toBe(true); + expect( + predicate(operators.EXACTLY)({ value: 0.5, other: 1 }), + ).toBe(false); + }); + + it('date', () => { + expect( + predicate(operators.EXACTLY)({ value: '2012-05-18', other: '2012-05-18' }), + ).toBe(true); + + expect( + predicate(operators.EXACTLY)({ value: '2012-05-18', other: '2012-05-19' }), + ).toBe(false); + }); }); - it('NOT', () => { - expect( - predicate(operators.NOT)({ value: 1, other: 1 }), - ).toBe(false); - expect( - predicate(operators.NOT)({ value: 2, other: 1 }), - ).toBe(true); - expect( - predicate(operators.NOT)({ value: null, other: false }), - ).toBe(true); - expect( - predicate(operators.NOT)({ value: null, other: true }), - ).toBe(true); - expect( - predicate(operators.NOT)({ value: false, other: null }), - ).toBe(true); - expect( - predicate(operators.NOT)({ value: false, other: true }), - ).toBe(true); - expect( - predicate(operators.NOT)({ value: true, other: false }), - ).toBe(true); + describe('NOT', () => { + it('number', () => { + expect( + predicate(operators.NOT)({ value: 1, other: 1 }), + ).toBe(false); + expect( + predicate(operators.NOT)({ value: 2, other: 1 }), + ).toBe(true); + expect( + predicate(operators.NOT)({ value: null, other: 0 }), + ).toBe(true); + }); + + it('string', () => { + expect( + predicate(operators.NOT)({ value: 'word', other: 'word' }), + ).toBe(false); + expect( + predicate(operators.NOT)({ value: 'not word', other: 'word' }), + ).toBe(true); + expect( + predicate(operators.NOT)({ value: null, other: 'word' }), + ).toBe(true); + }); + + it('boolean', () => { + expect( + predicate(operators.NOT)({ value: true, other: true }), + ).toBe(false); + expect( + predicate(operators.NOT)({ value: false, other: true }), + ).toBe(true); + expect( + predicate(operators.NOT)({ value: null, other: true }), + ).toBe(true); + expect( + predicate(operators.NOT)({ value: true, other: false }), + ).toBe(true); + expect( + predicate(operators.NOT)({ value: false, other: false }), + ).toBe(false); + expect( + predicate(operators.NOT)({ value: null, other: false }), + ).toBe(true); + expect( + predicate(operators.NOT)({ value: false, other: null }), + ).toBe(true); + }); + + it('categorical', () => { + expect( + predicate(operators.NOT)({ value: ['f'], other: ['f'] }), + ).toBe(false); + + expect( + predicate(operators.NOT)({ value: ['f'], other: ['f', 'm'] }), + ).toBe(true); + + // Order shouldn't matter + expect( + predicate(operators.NOT)({ value: ['f', 'm'], other: ['f', 'm'] }), + ).toBe(false); + + expect( + predicate(operators.NOT)({ value: ['m', 'f'], other: ['f', 'm'] }), + ).toBe(false); + + expect( + predicate(operators.NOT)({ value: [1], other: [1] }), + ).toBe(false); + + expect( + predicate(operators.NOT)({ value: [1], other: [1, 2] }), + ).toBe(true); + }); + + it('ordinal', () => { + expect( + predicate(operators.NOT)({ value: 'f', other: 'f' }), + ).toBe(false); + + expect( + predicate(operators.NOT)({ value: 'f', other: 'm' }), + ).toBe(true); + + expect( + predicate(operators.NOT)({ value: 1, other: 1 }), + ).toBe(false); + + expect( + predicate(operators.NOT)({ value: 1, other: 2 }), + ).toBe(true); + + expect( + predicate(operators.NOT)({ value: true, other: true }), + ).toBe(false); + + expect( + predicate(operators.NOT)({ value: true, other: false }), + ).toBe(true); + }); + + it('scalar', () => { + expect( + predicate(operators.NOT)({ value: 1, other: 1 }), + ).toBe(false); + expect( + predicate(operators.NOT)({ value: 0.5, other: 1 }), + ).toBe(true); + }); + + it('date', () => { + expect( + predicate(operators.NOT)({ value: '2012-05-18', other: '2012-05-18' }), + ).toBe(false); + + expect( + predicate(operators.NOT)({ value: '2012-05-18', other: '2012-05-19' }), + ).toBe(true); + }); }); - it('CONTAINS', () => { - expect( - predicate(operators.CONTAINS)({ value: 'word', other: 'wo' }), - ).toBe(true); - expect( - predicate(operators.CONTAINS)({ value: 'word', other: '^w' }), - ).toBe(true); - expect( - predicate(operators.CONTAINS)({ value: 'word', other: '^g' }), - ).toBe(false); + describe('CONTAINS', () => { + it('string', () => { + expect( + predicate(operators.CONTAINS)({ value: 'word', other: 'wo' }), + ).toBe(true); + expect( + predicate(operators.CONTAINS)({ value: 'word', other: '^w' }), + ).toBe(true); + expect( + predicate(operators.CONTAINS)({ value: 'word', other: '^g' }), + ).toBe(false); + }); }); - it('DOES_NOT_CONTAIN', () => { - expect( - predicate(operators.DOES_NOT_CONTAIN)({ value: 'word', other: 'go' }), - ).toBe(true); - expect( - predicate(operators.DOES_NOT_CONTAIN)({ value: 'word', other: '^g' }), - ).toBe(true); - expect( - predicate(operators.DOES_NOT_CONTAIN)({ value: 'word', other: '^w' }), - ).toBe(false); + describe('DOES_NOT_CONTAIN', () => { + it('string', () => { + expect( + predicate(operators.DOES_NOT_CONTAIN)({ value: 'word', other: 'go' }), + ).toBe(true); + expect( + predicate(operators.DOES_NOT_CONTAIN)({ value: 'word', other: '^g' }), + ).toBe(true); + expect( + predicate(operators.DOES_NOT_CONTAIN)({ value: 'word', other: '^w' }), + ).toBe(false); + }); }); it('EXISTS', () => { diff --git a/predicate.js b/predicate.js index bcdf9cb..cb9e5f6 100644 --- a/predicate.js +++ b/predicate.js @@ -6,18 +6,18 @@ const { // operators list const operators = { + GREATER_THAN: 'GREATER_THAN', + LESS_THAN: 'LESS_THAN', + GREATER_THAN_OR_EQUAL: 'GREATER_THAN_OR_EQUAL', + LESS_THAN_OR_EQUAL: 'LESS_THAN_OR_EQUAL', EXACTLY: 'EXACTLY', - INCLUDES: 'INCLUDES', - EXCLUDES: 'EXCLUDES', - EXISTS: 'EXISTS', - NOT_EXISTS: 'NOT_EXISTS', NOT: 'NOT', CONTAINS: 'CONTAINS', DOES_NOT_CONTAIN: 'DOES_NOT_CONTAIN', - GREATER_THAN: 'GREATER_THAN', - GREATER_THAN_OR_EQUAL: 'GREATER_THAN_OR_EQUAL', - LESS_THAN: 'LESS_THAN', - LESS_THAN_OR_EQUAL: 'LESS_THAN_OR_EQUAL', + EXISTS: 'EXISTS', + NOT_EXISTS: 'NOT_EXISTS', + INCLUDES: 'INCLUDES', + EXCLUDES: 'EXCLUDES', OPTIONS_GREATER_THAN: 'OPTIONS_GREATER_THAN', OPTIONS_LESS_THAN: 'OPTIONS_LESS_THAN', OPTIONS_EQUALS: 'OPTIONS_EQUALS', @@ -63,9 +63,19 @@ const predicate = operator => case countOperators.COUNT_LESS_THAN_OR_EQUAL: return value <= variableValue; case operators.EXACTLY: + // If value and variableValue are both arrays, sort them so that we can compare them + if (isArray(value) && isArray(variableValue)) { + return isEqual(value.sort(), variableValue.sort()); + } + return isEqual(value, variableValue); case countOperators.COUNT: return isEqual(value, variableValue); case operators.NOT: + // If value and variableValue are both arrays, sort them so that we can compare them + if (isArray(value) && isArray(variableValue)) { + return !isEqual(value.sort(), variableValue.sort()); + } + return !isEqual(value, variableValue); case countOperators.COUNT_NOT: return !isEqual(value, variableValue); case operators.CONTAINS: { From 08f2b9bcae4772062b27f152116875e9abca6f87 Mon Sep 17 00:00:00 2001 From: Caden Buckhalt Date: Wed, 21 Jun 2023 14:45:42 -0500 Subject: [PATCH 2/4] predicate exactly passes when attribute value is array with single item == value --- __tests__/predicate.test.js | 25 +++++++++++++++++++++++++ predicate.js | 4 ++++ 2 files changed, 29 insertions(+) diff --git a/__tests__/predicate.test.js b/__tests__/predicate.test.js index 855bbcb..87f42fa 100644 --- a/__tests__/predicate.test.js +++ b/__tests__/predicate.test.js @@ -223,6 +223,31 @@ describe('predicate', () => { expect( predicate(operators.EXACTLY)({ value: [1], other: [1, 2] }), ).toBe(false); + + // Pass when attribute value is array with single item and single item is value + expect( + predicate(operators.EXACTLY)({ value: 'f', other: ['f'] }), + ).toBe(true); + + expect( + predicate(operators.EXACTLY)({ value: 'f', other: ['m'] }), + ).toBe(false); + + expect( + predicate(operators.EXACTLY)({ value: 1, other: [1] }), + ).toBe(true); + + expect( + predicate(operators.EXACTLY)({ value: 1, other: [2] }), + ).toBe(false); + + expect( + predicate(operators.EXACTLY)({ value: 'f', other: ['f', 'm'] }), + ).toBe(false); + + expect( + predicate(operators.EXACTLY)({ value: 1, other: [1, 2] }), + ).toBe(false); }); it('ordinal', () => { diff --git a/predicate.js b/predicate.js index cb9e5f6..fa65d0d 100644 --- a/predicate.js +++ b/predicate.js @@ -67,6 +67,10 @@ const predicate = operator => if (isArray(value) && isArray(variableValue)) { return isEqual(value.sort(), variableValue.sort()); } + // if variableValue is an array, check if it is array with single item which == value + if (isArray(variableValue) && variableValue.length === 1) { + return isEqual(value, variableValue[0]); + } return isEqual(value, variableValue); case countOperators.COUNT: return isEqual(value, variableValue); From 0740cf3ef770788bc612969b41da81e719a4ca72 Mon Sep 17 00:00:00 2001 From: Caden Buckhalt Date: Fri, 23 Jun 2023 09:30:49 -0500 Subject: [PATCH 3/4] add comments to document bugfix --- __tests__/predicate.test.js | 13 ++++++++++++- predicate.js | 11 ++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/__tests__/predicate.test.js b/__tests__/predicate.test.js index 87f42fa..6ee0407 100644 --- a/__tests__/predicate.test.js +++ b/__tests__/predicate.test.js @@ -224,7 +224,18 @@ describe('predicate', () => { predicate(operators.EXACTLY)({ value: [1], other: [1, 2] }), ).toBe(false); - // Pass when attribute value is array with single item and single item is value + /** + * Expect true when attributeValue is an array with a single item + * and value is that single item + * + * Expect false if attributeValue is an array with multiple items + * and value is a single item + * + * Expect false if attributeValue is an array with single item + * and value is a different single item + * + * This checks that the bugfix in predicate is working + */ expect( predicate(operators.EXACTLY)({ value: 'f', other: ['f'] }), ).toBe(true); diff --git a/predicate.js b/predicate.js index fa65d0d..2906bf7 100644 --- a/predicate.js +++ b/predicate.js @@ -67,7 +67,16 @@ const predicate = operator => if (isArray(value) && isArray(variableValue)) { return isEqual(value.sort(), variableValue.sort()); } - // if variableValue is an array, check if it is array with single item which == value + + /** + * If variableValue is an array, check if it is array with single item + * which == value and return true + * + * This fixes a bug where categorical variable rules using exists/not were returning false + * because the variableValue was an array with a single item, but the value was not + * + * e.g. varaiableValue = ['F'], value = 'F' will return true + */ if (isArray(variableValue) && variableValue.length === 1) { return isEqual(value, variableValue[0]); } From 60bd3375a13be226d57b272851af81068fad4797 Mon Sep 17 00:00:00 2001 From: Caden Buckhalt Date: Fri, 23 Jun 2023 14:21:57 -0500 Subject: [PATCH 4/4] value is array, variableValue is single item --- __tests__/predicate.test.js | 26 +++++++++++++------------- predicate.js | 12 ++++++------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/__tests__/predicate.test.js b/__tests__/predicate.test.js index 6ee0407..f7fe6a0 100644 --- a/__tests__/predicate.test.js +++ b/__tests__/predicate.test.js @@ -225,39 +225,39 @@ describe('predicate', () => { ).toBe(false); /** - * Expect true when attributeValue is an array with a single item - * and value is that single item + * Expect true when value is an array with a single item + * and varaiableValue is that single item * - * Expect false if attributeValue is an array with multiple items - * and value is a single item + * Expect false if value is an array with multiple items + * and variableValue is a single item * - * Expect false if attributeValue is an array with single item - * and value is a different single item + * Expect false if value is an array with single item + * and variableValue is a different single item * - * This checks that the bugfix in predicate is working + * This checks that the categorical variable skip logic bugfix in predicate is working */ expect( - predicate(operators.EXACTLY)({ value: 'f', other: ['f'] }), + predicate(operators.EXACTLY)({ value: ['f'], other: 'f' }), ).toBe(true); expect( - predicate(operators.EXACTLY)({ value: 'f', other: ['m'] }), + predicate(operators.EXACTLY)({ value: ['f'], other: 'm' }), ).toBe(false); expect( - predicate(operators.EXACTLY)({ value: 1, other: [1] }), + predicate(operators.EXACTLY)({ value: [1], other: 1 }), ).toBe(true); expect( - predicate(operators.EXACTLY)({ value: 1, other: [2] }), + predicate(operators.EXACTLY)({ value: [1], other: 2 }), ).toBe(false); expect( - predicate(operators.EXACTLY)({ value: 'f', other: ['f', 'm'] }), + predicate(operators.EXACTLY)({ value: ['f', 'm'], other: 'f' }), ).toBe(false); expect( - predicate(operators.EXACTLY)({ value: 1, other: [1, 2] }), + predicate(operators.EXACTLY)({ value: [1, 2], other: 1 }), ).toBe(false); }); diff --git a/predicate.js b/predicate.js index 2906bf7..04aa6ad 100644 --- a/predicate.js +++ b/predicate.js @@ -69,16 +69,16 @@ const predicate = operator => } /** - * If variableValue is an array, check if it is array with single item - * which == value and return true + * If value is an array, check if it is array with single item + * which == variableValue and return true * * This fixes a bug where categorical variable rules using exists/not were returning false - * because the variableValue was an array with a single item, but the value was not + * because the value was an array with a single item, but the variableValue was not * - * e.g. varaiableValue = ['F'], value = 'F' will return true + * e.g. value = ['F'], variableValue = 'F' will return true */ - if (isArray(variableValue) && variableValue.length === 1) { - return isEqual(value, variableValue[0]); + if (isArray(value) && value.length === 1) { + return isEqual(value[0], variableValue); } return isEqual(value, variableValue); case countOperators.COUNT: