mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
fix(Compare Datasets Node): UI tweaks and fixes
This commit is contained in:
@@ -13,7 +13,7 @@ export class CompareDatasets implements INodeType {
|
|||||||
name: 'compareDatasets',
|
name: 'compareDatasets',
|
||||||
icon: 'file:compare.svg',
|
icon: 'file:compare.svg',
|
||||||
group: ['transform'],
|
group: ['transform'],
|
||||||
version: 1,
|
version: [1, 2],
|
||||||
description: 'Compare two inputs for changes',
|
description: 'Compare two inputs for changes',
|
||||||
defaults: { name: 'Compare Datasets' },
|
defaults: { name: 'Compare Datasets' },
|
||||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
|
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
|
||||||
@@ -94,6 +94,19 @@ export class CompareDatasets implements INodeType {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Fuzzy Compare',
|
||||||
|
name: 'fuzzyCompare',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description:
|
||||||
|
"Whether to tolerate small type differences when comparing fields. E.g. the number 3 and the string '3' are treated as the same.",
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
'@version': [2],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Prefer',
|
displayName: 'Prefer',
|
||||||
name: 'preferWhenMix',
|
name: 'preferWhenMix',
|
||||||
@@ -155,6 +168,11 @@ export class CompareDatasets implements INodeType {
|
|||||||
default: false,
|
default: false,
|
||||||
description:
|
description:
|
||||||
"Whether to tolerate small type differences when comparing fields. E.g. the number 3 and the string '3' are treated as the same.",
|
"Whether to tolerate small type differences when comparing fields. E.g. the number 3 and the string '3' are treated as the same.",
|
||||||
|
displayOptions: {
|
||||||
|
hide: {
|
||||||
|
'@version': [2],
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Disable Dot Notation',
|
displayName: 'Disable Dot Notation',
|
||||||
@@ -194,6 +212,12 @@ export class CompareDatasets implements INodeType {
|
|||||||
|
|
||||||
const options = this.getNodeParameter('options', 0, {});
|
const options = this.getNodeParameter('options', 0, {});
|
||||||
|
|
||||||
|
options.nodeVersion = this.getNode().typeVersion;
|
||||||
|
|
||||||
|
if (options.nodeVersion === 2) {
|
||||||
|
options.fuzzyCompare = this.getNodeParameter('fuzzyCompare', 0, false) as boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const input1 = checkInput(
|
const input1 = checkInput(
|
||||||
this.getInputData(0),
|
this.getInputData(0),
|
||||||
matchFields.map((pair) => pair.field1),
|
matchFields.map((pair) => pair.field1),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { IDataObject, INodeExecutionData } from 'n8n-workflow';
|
import type { IDataObject, INodeExecutionData } from 'n8n-workflow';
|
||||||
import { jsonParse } from 'n8n-workflow';
|
|
||||||
import { difference, get, intersection, isEmpty, isEqual, isNull, omit, set, union } from 'lodash';
|
import { difference, get, intersection, isEmpty, omit, set, union } from 'lodash';
|
||||||
|
import { fuzzyCompare } from '../../utils/utilities';
|
||||||
|
|
||||||
type PairToMatch = {
|
type PairToMatch = {
|
||||||
field1: string;
|
field1: string;
|
||||||
@@ -14,11 +15,18 @@ type EntryMatches = {
|
|||||||
|
|
||||||
type CompareFunction = <T, U>(a: T, b: U) => boolean;
|
type CompareFunction = <T, U>(a: T, b: U) => boolean;
|
||||||
|
|
||||||
|
const processNullishValueFunction = (version: number) => {
|
||||||
|
if (version === 2) {
|
||||||
|
return <T>(value: T) => (value === undefined ? null : value);
|
||||||
|
}
|
||||||
|
return <T>(value: T) => value || null;
|
||||||
|
};
|
||||||
|
|
||||||
function compareItems(
|
function compareItems(
|
||||||
item1: INodeExecutionData,
|
item1: INodeExecutionData,
|
||||||
item2: INodeExecutionData,
|
item2: INodeExecutionData,
|
||||||
fieldsToMatch: PairToMatch[],
|
fieldsToMatch: PairToMatch[],
|
||||||
resolve: string,
|
options: IDataObject,
|
||||||
skipFields: string[],
|
skipFields: string[],
|
||||||
isEntriesEqual: CompareFunction,
|
isEntriesEqual: CompareFunction,
|
||||||
) {
|
) {
|
||||||
@@ -46,20 +54,28 @@ function compareItems(
|
|||||||
const skipped: IDataObject = {};
|
const skipped: IDataObject = {};
|
||||||
|
|
||||||
differentKeys.forEach((key) => {
|
differentKeys.forEach((key) => {
|
||||||
switch (resolve) {
|
const processNullishValue = processNullishValueFunction(options.nodeVersion as number);
|
||||||
|
|
||||||
|
switch (options.resolve) {
|
||||||
case 'preferInput1':
|
case 'preferInput1':
|
||||||
different[key] = item1.json[key] || null;
|
different[key] = processNullishValue(item1.json[key]);
|
||||||
break;
|
break;
|
||||||
case 'preferInput2':
|
case 'preferInput2':
|
||||||
different[key] = item2.json[key] || null;
|
different[key] = processNullishValue(item2.json[key]);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
const input1 = item1.json[key] || null;
|
const input1 = processNullishValue(item1.json[key]);
|
||||||
const input2 = item2.json[key] || null;
|
const input2 = processNullishValue(item2.json[key]);
|
||||||
|
|
||||||
|
let [firstInputName, secondInputName] = ['input1', 'input2'];
|
||||||
|
if (options.nodeVersion === 2) {
|
||||||
|
[firstInputName, secondInputName] = ['inputA', 'inputB'];
|
||||||
|
}
|
||||||
|
|
||||||
if (skipFields.includes(key)) {
|
if (skipFields.includes(key)) {
|
||||||
skipped[key] = { input1, input2 };
|
skipped[key] = { [firstInputName]: input1, [secondInputName]: input2 };
|
||||||
} else {
|
} else {
|
||||||
different[key] = { input1, input2 };
|
different[key] = { [firstInputName]: input1, [secondInputName]: input2 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -160,93 +176,6 @@ function findFirstMatch(
|
|||||||
return [{ entry: data[index], index }];
|
return [{ entry: data[index], index }];
|
||||||
}
|
}
|
||||||
|
|
||||||
const parseStringAndCompareToObject = (str: string, arr: IDataObject) => {
|
|
||||||
try {
|
|
||||||
const parsedArray = jsonParse(str);
|
|
||||||
return isEqual(parsedArray, arr);
|
|
||||||
} catch (error) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function isFalsy<T>(value: T) {
|
|
||||||
if (isNull(value)) return true;
|
|
||||||
if (typeof value === 'string' && value === '') return true;
|
|
||||||
if (Array.isArray(value) && value.length === 0) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fuzzyCompare =
|
|
||||||
(options: IDataObject) =>
|
|
||||||
<T, U>(item1: T, item2: U) => {
|
|
||||||
//Fuzzy compare is disabled, so we do strict comparison
|
|
||||||
if (!options.fuzzyCompare) return isEqual(item1, item2);
|
|
||||||
|
|
||||||
//Both types are the same, so we do strict comparison
|
|
||||||
if (!isNull(item1) && !isNull(item2) && typeof item1 === typeof item2) {
|
|
||||||
return isEqual(item1, item2);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Null, empty strings, empty arrays all treated as the same
|
|
||||||
if (isFalsy(item1) && isFalsy(item2)) return true;
|
|
||||||
|
|
||||||
//When a field is missing in one branch and isFalsy() in another, treat them as matching
|
|
||||||
if (isFalsy(item1) && item2 === undefined) return true;
|
|
||||||
if (item1 === undefined && isFalsy(item2)) return true;
|
|
||||||
|
|
||||||
//Compare numbers and strings representing that number
|
|
||||||
if (typeof item1 === 'number' && typeof item2 === 'string') {
|
|
||||||
return item1.toString() === item2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof item1 === 'string' && typeof item2 === 'number') {
|
|
||||||
return item1 === item2.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
//Compare objects/arrays and their stringified version
|
|
||||||
if (!isNull(item1) && typeof item1 === 'object' && typeof item2 === 'string') {
|
|
||||||
return parseStringAndCompareToObject(item2, item1 as IDataObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isNull(item2) && typeof item1 === 'string' && typeof item2 === 'object') {
|
|
||||||
return parseStringAndCompareToObject(item1, item2 as IDataObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Compare booleans and strings representing the boolean (’true’, ‘True’, ‘TRUE’)
|
|
||||||
if (typeof item1 === 'boolean' && typeof item2 === 'string') {
|
|
||||||
if (item1 === true && item2.toLocaleLowerCase() === 'true') return true;
|
|
||||||
if (item1 === false && item2.toLocaleLowerCase() === 'false') return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof item2 === 'boolean' && typeof item1 === 'string') {
|
|
||||||
if (item2 === true && item1.toLocaleLowerCase() === 'true') return true;
|
|
||||||
if (item2 === false && item1.toLocaleLowerCase() === 'false') return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Compare booleans and the numbers/string 0 and 1
|
|
||||||
if (typeof item1 === 'boolean' && typeof item2 === 'number') {
|
|
||||||
if (item1 === true && item2 === 1) return true;
|
|
||||||
if (item1 === false && item2 === 0) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof item2 === 'boolean' && typeof item1 === 'number') {
|
|
||||||
if (item2 === true && item1 === 1) return true;
|
|
||||||
if (item2 === false && item1 === 0) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof item1 === 'boolean' && typeof item2 === 'string') {
|
|
||||||
if (item1 === true && item2 === '1') return true;
|
|
||||||
if (item1 === false && item2 === '0') return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof item2 === 'boolean' && typeof item1 === 'string') {
|
|
||||||
if (item2 === true && item1 === '1') return true;
|
|
||||||
if (item2 === false && item1 === '0') return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return isEqual(item1, item2);
|
|
||||||
};
|
|
||||||
|
|
||||||
export function findMatches(
|
export function findMatches(
|
||||||
input1: INodeExecutionData[],
|
input1: INodeExecutionData[],
|
||||||
input2: INodeExecutionData[],
|
input2: INodeExecutionData[],
|
||||||
@@ -256,7 +185,12 @@ export function findMatches(
|
|||||||
const data1 = [...input1];
|
const data1 = [...input1];
|
||||||
const data2 = [...input2];
|
const data2 = [...input2];
|
||||||
|
|
||||||
const isEntriesEqual = fuzzyCompare(options);
|
let compareVersion = 1;
|
||||||
|
if (options.nodeVersion === 2) {
|
||||||
|
compareVersion = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isEntriesEqual = fuzzyCompare(options.fuzzyCompare as boolean, compareVersion);
|
||||||
const disableDotNotation = (options.disableDotNotation as boolean) || false;
|
const disableDotNotation = (options.disableDotNotation as boolean) || false;
|
||||||
const multipleMatches = (options.multipleMatches as string) || 'first';
|
const multipleMatches = (options.multipleMatches as string) || 'first';
|
||||||
const skipFields = ((options.skipFields as string) || '').split(',').map((field) => field.trim());
|
const skipFields = ((options.skipFields as string) || '').split(',').map((field) => field.trim());
|
||||||
@@ -370,7 +304,7 @@ export function findMatches(
|
|||||||
entryMatches.entry,
|
entryMatches.entry,
|
||||||
match,
|
match,
|
||||||
fieldsToMatch,
|
fieldsToMatch,
|
||||||
options.resolve as string,
|
options,
|
||||||
skipFields,
|
skipFields,
|
||||||
isEntriesEqual,
|
isEntriesEqual,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import type {
|
|||||||
INodeExecutionData,
|
INodeExecutionData,
|
||||||
IPairedItemData,
|
IPairedItemData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { jsonParse } from 'n8n-workflow';
|
|
||||||
|
|
||||||
import { assign, assignWith, get, isEqual, isNull, merge, mergeWith } from 'lodash';
|
import { assign, assignWith, get, merge, mergeWith } from 'lodash';
|
||||||
|
import { fuzzyCompare } from '../../../utils/utilities';
|
||||||
|
|
||||||
type PairToMatch = {
|
type PairToMatch = {
|
||||||
field1: string;
|
field1: string;
|
||||||
@@ -122,93 +122,6 @@ function findFirstMatch(
|
|||||||
return [{ entry: data[index], index }];
|
return [{ entry: data[index], index }];
|
||||||
}
|
}
|
||||||
|
|
||||||
const parseStringAndCompareToObject = (str: string, arr: IDataObject) => {
|
|
||||||
try {
|
|
||||||
const parsedArray = jsonParse(str);
|
|
||||||
return isEqual(parsedArray, arr);
|
|
||||||
} catch (error) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function isFalsy<T>(value: T) {
|
|
||||||
if (isNull(value)) return true;
|
|
||||||
if (typeof value === 'string' && value === '') return true;
|
|
||||||
if (Array.isArray(value) && value.length === 0) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fuzzyCompare =
|
|
||||||
(options: IDataObject) =>
|
|
||||||
<T, U>(item1: T, item2: U) => {
|
|
||||||
//Fuzzy compare is disabled, so we do strict comparison
|
|
||||||
if (!options.fuzzyCompare) return isEqual(item1, item2);
|
|
||||||
|
|
||||||
//Both types are the same, so we do strict comparison
|
|
||||||
if (!isNull(item1) && !isNull(item2) && typeof item1 === typeof item2) {
|
|
||||||
return isEqual(item1, item2);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Null, empty strings, empty arrays all treated as the same
|
|
||||||
if (isFalsy(item1) && isFalsy(item2)) return true;
|
|
||||||
|
|
||||||
//When a field is missing in one branch and isFalsy() in another, treat them as matching
|
|
||||||
if (isFalsy(item1) && item2 === undefined) return true;
|
|
||||||
if (item1 === undefined && isFalsy(item2)) return true;
|
|
||||||
|
|
||||||
//Compare numbers and strings representing that number
|
|
||||||
if (typeof item1 === 'number' && typeof item2 === 'string') {
|
|
||||||
return item1.toString() === item2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof item1 === 'string' && typeof item2 === 'number') {
|
|
||||||
return item1 === item2.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
//Compare objects/arrays and their stringified version
|
|
||||||
if (!isNull(item1) && typeof item1 === 'object' && typeof item2 === 'string') {
|
|
||||||
return parseStringAndCompareToObject(item2, item1 as IDataObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isNull(item2) && typeof item1 === 'string' && typeof item2 === 'object') {
|
|
||||||
return parseStringAndCompareToObject(item1, item2 as IDataObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Compare booleans and strings representing the boolean (’true’, ‘True’, ‘TRUE’)
|
|
||||||
if (typeof item1 === 'boolean' && typeof item2 === 'string') {
|
|
||||||
if (item1 === true && item2.toLocaleLowerCase() === 'true') return true;
|
|
||||||
if (item1 === false && item2.toLocaleLowerCase() === 'false') return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof item2 === 'boolean' && typeof item1 === 'string') {
|
|
||||||
if (item2 === true && item1.toLocaleLowerCase() === 'true') return true;
|
|
||||||
if (item2 === false && item1.toLocaleLowerCase() === 'false') return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Compare booleans and the numbers/string 0 and 1
|
|
||||||
if (typeof item1 === 'boolean' && typeof item2 === 'number') {
|
|
||||||
if (item1 === true && item2 === 1) return true;
|
|
||||||
if (item1 === false && item2 === 0) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof item2 === 'boolean' && typeof item1 === 'number') {
|
|
||||||
if (item2 === true && item1 === 1) return true;
|
|
||||||
if (item2 === false && item1 === 0) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof item1 === 'boolean' && typeof item2 === 'string') {
|
|
||||||
if (item1 === true && item2 === '1') return true;
|
|
||||||
if (item1 === false && item2 === '0') return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof item2 === 'boolean' && typeof item1 === 'string') {
|
|
||||||
if (item2 === true && item1 === '1') return true;
|
|
||||||
if (item2 === false && item1 === '0') return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return isEqual(item1, item2);
|
|
||||||
};
|
|
||||||
|
|
||||||
export function findMatches(
|
export function findMatches(
|
||||||
input1: INodeExecutionData[],
|
input1: INodeExecutionData[],
|
||||||
input2: INodeExecutionData[],
|
input2: INodeExecutionData[],
|
||||||
@@ -222,7 +135,7 @@ export function findMatches(
|
|||||||
[data1, data2] = [data2, data1];
|
[data1, data2] = [data2, data1];
|
||||||
}
|
}
|
||||||
|
|
||||||
const isEntriesEqual = fuzzyCompare(options);
|
const isEntriesEqual = fuzzyCompare(options.fuzzyCompare as boolean);
|
||||||
const disableDotNotation = options.disableDotNotation || false;
|
const disableDotNotation = options.disableDotNotation || false;
|
||||||
const multipleMatches = (options.multipleMatches as string) || 'all';
|
const multipleMatches = (options.multipleMatches as string) || 'all';
|
||||||
|
|
||||||
|
|||||||
31
packages/nodes-base/test/utils/utilities.test.ts
Normal file
31
packages/nodes-base/test/utils/utilities.test.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { fuzzyCompare } from '../../utils/utilities';
|
||||||
|
|
||||||
|
//most test cases for fuzzyCompare are done in Compare Datasets node tests
|
||||||
|
describe('Test fuzzyCompare', () => {
|
||||||
|
it('should do strict comparison', () => {
|
||||||
|
const compareFunction = fuzzyCompare(false);
|
||||||
|
|
||||||
|
expect(compareFunction(1, '1')).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should do fuzzy comparison', () => {
|
||||||
|
const compareFunction = fuzzyCompare(true);
|
||||||
|
|
||||||
|
expect(compareFunction(1, '1')).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should treat null, 0 and "0" as equal', () => {
|
||||||
|
const compareFunction = fuzzyCompare(true, 2);
|
||||||
|
|
||||||
|
expect(compareFunction(null, null)).toEqual(true);
|
||||||
|
expect(compareFunction(null, 0)).toEqual(true);
|
||||||
|
expect(compareFunction(null, '0')).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not treat null, 0 and "0" as equal', () => {
|
||||||
|
const compareFunction = fuzzyCompare(true);
|
||||||
|
|
||||||
|
expect(compareFunction(null, 0)).toEqual(false);
|
||||||
|
expect(compareFunction(null, '0')).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { IDisplayOptions, INodeProperties } from 'n8n-workflow';
|
import type { IDataObject, IDisplayOptions, INodeProperties } from 'n8n-workflow';
|
||||||
|
import { jsonParse } from 'n8n-workflow';
|
||||||
|
|
||||||
import { merge } from 'lodash';
|
import { isEqual, isNull, merge } from 'lodash';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an array of elements split into groups the length of `size`.
|
* Creates an array of elements split into groups the length of `size`.
|
||||||
@@ -71,3 +72,103 @@ export function updateDisplayOptions(
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isFalsy<T>(value: T) {
|
||||||
|
if (isNull(value)) return true;
|
||||||
|
if (typeof value === 'string' && value === '') return true;
|
||||||
|
if (Array.isArray(value) && value.length === 0) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseStringAndCompareToObject = (str: string, arr: IDataObject) => {
|
||||||
|
try {
|
||||||
|
const parsedArray = jsonParse(str);
|
||||||
|
return isEqual(parsedArray, arr);
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fuzzyCompare = (useFuzzyCompare: boolean, compareVersion = 1) => {
|
||||||
|
if (!useFuzzyCompare) {
|
||||||
|
//Fuzzy compare is false we do strict comparison
|
||||||
|
return <T, U>(item1: T, item2: U) => isEqual(item1, item2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <T, U>(item1: T, item2: U) => {
|
||||||
|
//Both types are the same, so we do strict comparison
|
||||||
|
if (!isNull(item1) && !isNull(item2) && typeof item1 === typeof item2) {
|
||||||
|
return isEqual(item1, item2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compareVersion >= 2) {
|
||||||
|
//Null, 0 and "0" treated as equal
|
||||||
|
if (isNull(item1) && (isNull(item2) || item2 === 0 || item2 === '0')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNull(item2) && (isNull(item1) || item1 === 0 || item1 === '0')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Null, empty strings, empty arrays all treated as the same
|
||||||
|
if (isFalsy(item1) && isFalsy(item2)) return true;
|
||||||
|
|
||||||
|
//When a field is missing in one branch and isFalsy() in another, treat them as matching
|
||||||
|
if (isFalsy(item1) && item2 === undefined) return true;
|
||||||
|
if (item1 === undefined && isFalsy(item2)) return true;
|
||||||
|
|
||||||
|
//Compare numbers and strings representing that number
|
||||||
|
if (typeof item1 === 'number' && typeof item2 === 'string') {
|
||||||
|
return item1.toString() === item2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof item1 === 'string' && typeof item2 === 'number') {
|
||||||
|
return item1 === item2.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Compare objects/arrays and their stringified version
|
||||||
|
if (!isNull(item1) && typeof item1 === 'object' && typeof item2 === 'string') {
|
||||||
|
return parseStringAndCompareToObject(item2, item1 as IDataObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isNull(item2) && typeof item1 === 'string' && typeof item2 === 'object') {
|
||||||
|
return parseStringAndCompareToObject(item1, item2 as IDataObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Compare booleans and strings representing the boolean (’true’, ‘True’, ‘TRUE’)
|
||||||
|
if (typeof item1 === 'boolean' && typeof item2 === 'string') {
|
||||||
|
if (item1 === true && item2.toLocaleLowerCase() === 'true') return true;
|
||||||
|
if (item1 === false && item2.toLocaleLowerCase() === 'false') return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof item2 === 'boolean' && typeof item1 === 'string') {
|
||||||
|
if (item2 === true && item1.toLocaleLowerCase() === 'true') return true;
|
||||||
|
if (item2 === false && item1.toLocaleLowerCase() === 'false') return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Compare booleans and the numbers/string 0 and 1
|
||||||
|
if (typeof item1 === 'boolean' && typeof item2 === 'number') {
|
||||||
|
if (item1 === true && item2 === 1) return true;
|
||||||
|
if (item1 === false && item2 === 0) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof item2 === 'boolean' && typeof item1 === 'number') {
|
||||||
|
if (item2 === true && item1 === 1) return true;
|
||||||
|
if (item2 === false && item1 === 0) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof item1 === 'boolean' && typeof item2 === 'string') {
|
||||||
|
if (item1 === true && item2 === '1') return true;
|
||||||
|
if (item1 === false && item2 === '0') return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof item2 === 'boolean' && typeof item1 === 'string') {
|
||||||
|
if (item2 === true && item1 === '1') return true;
|
||||||
|
if (item2 === false && item1 === '0') return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isEqual(item1, item2);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user