diff --git a/docs/object-functions.md b/docs/object-functions.md index 6dc8b618..3f4d693d 100644 --- a/docs/object-functions.md +++ b/docs/object-functions.md @@ -76,4 +76,119 @@ Evaluates the type of `value` and returns one of the following strings: * `"function"` Returns (non-string) `undefined` when `value` is `undefined`. +## `$removeEmpty()` +__Signature:__ `$removeEmpty(input, exclusionList?, removeEmptyStrings?)` + +Recursively removes empty values from objects and arrays, creating a cleaned version of the input data structure. Empty objects `{}`, empty arrays `[]`, `null` values, and optionally empty strings `""` are removed. The function preserves the original structure while eliminating empty elements. + +**Parameters:** +- `input` - The data structure to clean up (object, array, or primitive value) +- `exclusionList` (optional) - Array of dot-notation paths to exclude from cleaning. Paths can optionally start with `$.` +- `removeEmptyStrings` (optional) - Boolean flag to control removal of empty strings (default: `true`) + +**Examples** + +Basic cleanup of an object: +``` +$removeEmpty({ + "name": "John", + "email": "", + "profile": { + "bio": null, + "settings": {}, + "preferences": [] + }, + "active": true +}) +``` +=> +``` +{ + "name": "John", + "active": true +} +``` + +Cleanup with exclusions to preserve specific empty values: +``` +$removeEmpty({ + "required": "", + "optional": "", + "config": {} +}, ["required", "config"]) +``` +=> +``` +{ + "required": "", + "config": {} +} +``` + +Preserving empty strings by setting `removeEmptyStrings` to `false`: +``` +$removeEmpty({ + "name": "Jane", + "middle": "", + "last": "Doe", + "metadata": null +}, [], false) +``` +=> +``` +{ + "name": "Jane", + "middle": "", + "last": "Doe" +} +``` + +Cleaning arrays with mixed content: +``` +$removeEmpty([ + "valid", + "", + null, + {"key": "value"}, + {}, + [], + 42 +]) +``` +=> +``` +[ + "valid", + {"key": "value"}, + 42 +] +``` + +Using path exclusions with nested objects: +``` +$removeEmpty({ + "user": { + "name": "Alice", + "bio": "", + "social": { + "twitter": "", + "github": "alice123" + } + } +}, ["user.bio", "user.social.twitter"]) +``` +=> +``` +{ + "user": { + "name": "Alice", + "bio": "", + "social": { + "twitter": "", + "github": "alice123" + } + } +} +``` + diff --git a/src/functions.js b/src/functions.js index 3b28e943..d17224df 100644 --- a/src/functions.js +++ b/src/functions.js @@ -2056,6 +2056,92 @@ const functions = (() => { return result; } + /** + * Clean up empty values from an object or array + * @param {*} input - The input value to clean + * @param {Array} exclusionList - Array of paths to exclude from cleaning (optional) + * @param {boolean} removeEmptyStrings - Whether to remove empty strings (optional, default true) + * @param {string} currentPath - Current path for internal recursion (optional) + * @returns {*} Cleaned input with empty objects, arrays, and optionally strings removed + */ + function removeEmpty(input, exclusionList, removeEmptyStrings, currentPath) { + // Handle undefined input + if (typeof input === 'undefined') { + return undefined; + } + + // Set default values + exclusionList = exclusionList || []; + removeEmptyStrings = (removeEmptyStrings !== false); // default to true + currentPath = currentPath || ''; + + // Helper to check if currentPath matches any exclusion path + var isExcluded = exclusionList.some(function(exclusionPath) { + var normalizedExclusionPath = exclusionPath.indexOf('$.') === 0 ? exclusionPath.slice(2) : exclusionPath; + return normalizedExclusionPath === currentPath; + }); + + if (isExcluded) { + // If the current path is excluded, return the object/array/value as is, without cleaning children + return input; + } + + if (Array.isArray(input)) { + var cleanedArray = input + .map(function(item, idx) { + return removeEmpty(item, exclusionList, removeEmptyStrings, currentPath + '[' + idx + ']'); + }) + .filter(function(item) { + // Filter out undefined values + if (typeof item === 'undefined') { + return false; + } + // Filter out empty objects and arrays + if (typeof item === 'object' && item !== null) { + return Object.keys(item).length > 0 || (Array.isArray(item) && item.length > 0); + } + // Keep everything else (including null, strings, numbers, booleans) + return true; + }); + return cleanedArray; + } else if (typeof input === 'object' && input !== null) { + var cleanedObject = {}; + var keys = Object.keys(input); + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + var nextPath = currentPath ? currentPath + '.' + key : key; + // Check if the nextPath is excluded before recursing + var isChildExcluded = exclusionList.some(function(exclusionPath) { + var normalizedExclusionPath = exclusionPath.indexOf('$.') === 0 ? exclusionPath.slice(2) : exclusionPath; + return normalizedExclusionPath === nextPath; + }); + if (isChildExcluded) { + cleanedObject[key] = input[key]; + } else { + var value = removeEmpty(input[key], exclusionList, removeEmptyStrings, nextPath); + if ( + typeof value !== 'undefined' && + value !== null && + !(typeof value === 'object' && value !== null && Object.keys(value).length === 0) && + !(Array.isArray(value) && value.length === 0) && + !(removeEmptyStrings && value === '') + ) { + cleanedObject[key] = value; + } + } + } + // Return undefined if the cleaned object is empty + return Object.keys(cleanedObject).length === 0 ? undefined : cleanedObject; + } + + // Handle empty strings + if (removeEmptyStrings && input === '') { + return undefined; + } + + return input; + } + return { sum, count, max, min, average, string, substring, substringBefore, substringAfter, lowercase, uppercase, length, trim, pad, @@ -2064,7 +2150,8 @@ const functions = (() => { boolean, not, map, zip, filter, single, foldLeft, sift, keys, lookup, append, exists, spread, merge, reverse, each, error, assert, type, sort, shuffle, distinct, - base64encode, base64decode, encodeUrlComponent, encodeUrl, decodeUrlComponent, decodeUrl + base64encode, base64decode, encodeUrlComponent, encodeUrl, decodeUrlComponent, decodeUrl, + removeEmpty }; })(); diff --git a/src/jsonata.js b/src/jsonata.js index c21eb1e2..baaaf77b 100644 --- a/src/jsonata.js +++ b/src/jsonata.js @@ -1929,6 +1929,7 @@ var jsonata = (function() { staticFrame.bind('encodeUrl', defineFunction(fn.encodeUrl, '')); staticFrame.bind('decodeUrlComponent', defineFunction(fn.decodeUrlComponent, '')); staticFrame.bind('decodeUrl', defineFunction(fn.decodeUrl, '')); + staticFrame.bind('removeEmpty', defineFunction(fn.removeEmpty, '?b?:j>')); staticFrame.bind('eval', defineFunction(functionEval, '')); staticFrame.bind('toMillis', defineFunction(datetime.toMillis, '')); staticFrame.bind('fromMillis', defineFunction(datetime.fromMillis, '')); diff --git a/test/test-suite/datasets/dataset25.json b/test/test-suite/datasets/dataset25.json new file mode 100644 index 00000000..c568dd0a --- /dev/null +++ b/test/test-suite/datasets/dataset25.json @@ -0,0 +1,87 @@ +{ + "user": { + "name": "John Doe", + "email": "john@example.com", + "profile": { + "bio": "", + "website": null, + "social": { + "twitter": "", + "linkedin": "https://linkedin.com/in/johndoe", + "facebook": null + }, + "preferences": { + "notifications": {}, + "privacy": { + "public": true, + "settings": [] + } + } + }, + "contacts": [], + "projects": [ + { + "name": "Project Alpha", + "description": "A sample project", + "tags": ["work", "important"], + "metadata": { + "created": "2023-01-15", + "updated": null, + "notes": "" + } + }, + { + "name": "", + "description": null, + "tags": [], + "metadata": {} + } + ], + "settings": { + "theme": "dark", + "language": "en", + "features": { + "beta": false, + "experimental": {} + } + } + }, + "organization": { + "name": "ACME Corp", + "departments": [ + { + "name": "Engineering", + "employees": [ + { + "name": "Alice Smith", + "role": "Developer", + "skills": ["JavaScript", "Python"], + "certifications": [] + }, + { + "name": "", + "role": null, + "skills": [], + "certifications": null + } + ], + "budget": { + "allocated": 100000, + "spent": 0, + "remaining": null + } + }, + { + "name": "", + "employees": [], + "budget": {} + } + ], + "locations": [], + "policies": { + "remote": true, + "benefits": {}, + "training": [] + } + } +} diff --git a/test/test-suite/datasets/dataset26.json b/test/test-suite/datasets/dataset26.json new file mode 100644 index 00000000..a47fbb12 --- /dev/null +++ b/test/test-suite/datasets/dataset26.json @@ -0,0 +1,65 @@ +{ + "data": { + "arrays": { + "empty": [], + "mixed": [1, "", null, {}, [], "valid"], + "nested": [ + { + "id": 1, + "value": "test", + "meta": {} + }, + { + "id": null, + "value": "", + "meta": { + "tags": [], + "flags": {} + } + }, + [] + ] + }, + "objects": { + "empty": {}, + "partial": { + "valid": "data", + "empty_string": "", + "null_value": null, + "empty_object": {}, + "empty_array": [] + }, + "nested": { + "level1": { + "level2": { + "level3": { + "data": "", + "more": {} + } + }, + "other": null + } + } + }, + "primitives": { + "string": "valid", + "empty_string": "", + "null": null, + "number": 42, + "zero": 0, + "boolean": true, + "false": false + }, + "exclusion_test": { + "keep_empty_string": "", + "keep_empty_object": {}, + "keep_empty_array": [], + "nested_exclusion": { + "deep": { + "empty_to_keep": "", + "empty_to_remove": {} + } + } + } + } +} diff --git a/test/test-suite/groups/function-removeEmpty/case000.json b/test/test-suite/groups/function-removeEmpty/case000.json new file mode 100644 index 00000000..1eef7f65 --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case000.json @@ -0,0 +1,6 @@ +{ + "expr": "[$removeEmpty(undefined), $removeEmpty(null), $removeEmpty(\"test string\"), $removeEmpty(\"\"), $removeEmpty(42), $removeEmpty({}), $removeEmpty([])]", + "dataset": "dataset25", + "bindings": {}, + "result": [null, "test string", 42] +} diff --git a/test/test-suite/groups/function-removeEmpty/case001.json b/test/test-suite/groups/function-removeEmpty/case001.json new file mode 100644 index 00000000..41ffde39 --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case001.json @@ -0,0 +1,6 @@ +{ + "expr": "$removeEmpty(\"\", [], false)", + "dataset": "dataset25", + "bindings": {}, + "result": "" +} diff --git a/test/test-suite/groups/function-removeEmpty/case002.json b/test/test-suite/groups/function-removeEmpty/case002.json new file mode 100644 index 00000000..0116ce9c --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case002.json @@ -0,0 +1,8 @@ +{ + "expr": "$removeEmpty({\"valid\": \"data\", \"empty\": \"\", \"null\": null, \"emptyObj\": {}, \"emptyArr\": []})", + "dataset": "dataset25", + "bindings": {}, + "result": { + "valid": "data" + } +} diff --git a/test/test-suite/groups/function-removeEmpty/case003.json b/test/test-suite/groups/function-removeEmpty/case003.json new file mode 100644 index 00000000..0f30ae0b --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case003.json @@ -0,0 +1,6 @@ +{ + "expr": "$removeEmpty([1, \"\", null, {}, [], \"valid\"])", + "dataset": "dataset25", + "bindings": {}, + "result": [1, null, "valid"] +} diff --git a/test/test-suite/groups/function-removeEmpty/case004.json b/test/test-suite/groups/function-removeEmpty/case004.json new file mode 100644 index 00000000..4bac5c1b --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case004.json @@ -0,0 +1,8 @@ +{ + "expr": "$removeEmpty(data.objects.partial)", + "dataset": "dataset26", + "bindings": {}, + "result": { + "valid": "data" + } +} diff --git a/test/test-suite/groups/function-removeEmpty/case005.json b/test/test-suite/groups/function-removeEmpty/case005.json new file mode 100644 index 00000000..75b9c469 --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case005.json @@ -0,0 +1,8 @@ +{ + "expr": "$removeEmpty({\"keep\": \"\", \"remove\": \"\"}, [\"keep\"])", + "dataset": "dataset25", + "bindings": {}, + "result": { + "keep": "" + } +} diff --git a/test/test-suite/groups/function-removeEmpty/case006.json b/test/test-suite/groups/function-removeEmpty/case006.json new file mode 100644 index 00000000..b68d604e --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case006.json @@ -0,0 +1,9 @@ +{ + "expr": "$removeEmpty(data.exclusion_test, [\"keep_empty_string\", \"keep_empty_object\"])", + "dataset": "dataset26", + "bindings": {}, + "result": { + "keep_empty_string": "", + "keep_empty_object": {} + } +} diff --git a/test/test-suite/groups/function-removeEmpty/case007.json b/test/test-suite/groups/function-removeEmpty/case007.json new file mode 100644 index 00000000..8685dd64 --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case007.json @@ -0,0 +1,12 @@ +{ + "expr": "$removeEmpty(data.exclusion_test, [\"nested_exclusion.deep.empty_to_keep\"])", + "dataset": "dataset26", + "bindings": {}, + "result": { + "nested_exclusion": { + "deep": { + "empty_to_keep": "" + } + } + } +} diff --git a/test/test-suite/groups/function-removeEmpty/case008.json b/test/test-suite/groups/function-removeEmpty/case008.json new file mode 100644 index 00000000..f1c31002 --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case008.json @@ -0,0 +1,8 @@ +{ + "expr": "$removeEmpty(data.exclusion_test, [\"$.keep_empty_array\"])", + "dataset": "dataset26", + "bindings": {}, + "result": { + "keep_empty_array": [] + } +} diff --git a/test/test-suite/groups/function-removeEmpty/case009.json b/test/test-suite/groups/function-removeEmpty/case009.json new file mode 100644 index 00000000..b717f6b3 --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case009.json @@ -0,0 +1,38 @@ +{ + "expr": "$removeEmpty(user, [\"profile.social.twitter\", \"contacts\"])", + "dataset": "dataset25", + "bindings": {}, + "result": { + "name": "John Doe", + "email": "john@example.com", + "profile": { + "social": { + "linkedin": "https://linkedin.com/in/johndoe", + "twitter": "" + }, + "preferences": { + "privacy": { + "public": true + } + } + }, + "contacts": [], + "projects": [ + { + "name": "Project Alpha", + "description": "A sample project", + "tags": ["work", "important"], + "metadata": { + "created": "2023-01-15" + } + } + ], + "settings": { + "theme": "dark", + "language": "en", + "features": { + "beta": false + } + } + } +} diff --git a/test/test-suite/groups/function-removeEmpty/case010.json b/test/test-suite/groups/function-removeEmpty/case010.json new file mode 100644 index 00000000..53070f09 --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case010.json @@ -0,0 +1,11 @@ +{ + "expr": "$removeEmpty(data.arrays.nested)", + "dataset": "dataset26", + "bindings": {}, + "result": [ + { + "id": 1, + "value": "test" + } + ] +} diff --git a/test/test-suite/groups/function-removeEmpty/case011.json b/test/test-suite/groups/function-removeEmpty/case011.json new file mode 100644 index 00000000..5824cb88 --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case011.json @@ -0,0 +1,6 @@ +{ + "expr": "$removeEmpty(data.objects.nested)", + "dataset": "dataset26", + "bindings": {}, + "undefinedResult": true +} diff --git a/test/test-suite/groups/function-removeEmpty/case012.json b/test/test-suite/groups/function-removeEmpty/case012.json new file mode 100644 index 00000000..e656bff9 --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case012.json @@ -0,0 +1,21 @@ +{ + "expr": "$removeEmpty(organization.departments)", + "dataset": "dataset25", + "bindings": {}, + "result": [ + { + "name": "Engineering", + "employees": [ + { + "name": "Alice Smith", + "role": "Developer", + "skills": ["JavaScript", "Python"] + } + ], + "budget": { + "allocated": 100000, + "spent": 0 + } + } + ] +} diff --git a/test/test-suite/groups/function-removeEmpty/case013.json b/test/test-suite/groups/function-removeEmpty/case013.json new file mode 100644 index 00000000..20e94e28 --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case013.json @@ -0,0 +1,12 @@ +{ + "expr": "$removeEmpty(data.primitives)", + "dataset": "dataset26", + "bindings": {}, + "result": { + "string": "valid", + "number": 42, + "zero": 0, + "boolean": true, + "false": false + } +} diff --git a/test/test-suite/groups/function-removeEmpty/case014.json b/test/test-suite/groups/function-removeEmpty/case014.json new file mode 100644 index 00000000..190a37a4 --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case014.json @@ -0,0 +1,13 @@ +{ + "expr": "$removeEmpty(data.primitives, [], false)", + "dataset": "dataset26", + "bindings": {}, + "result": { + "string": "valid", + "empty_string": "", + "number": 42, + "zero": 0, + "boolean": true, + "false": false + } +} diff --git a/test/test-suite/groups/function-removeEmpty/case015.json b/test/test-suite/groups/function-removeEmpty/case015.json new file mode 100644 index 00000000..37fe7087 --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case015.json @@ -0,0 +1,13 @@ +{ + "expr": "[$removeEmpty({\"empty\": \"\", \"null\": null, \"emptyObj\": {}}, [\"\"]), $removeEmpty([[1, \"\", null, {}, []]], [\"$.\"])]", + "dataset": "dataset25", + "bindings": {}, + "result": [ + { + "empty": "", + "null": null, + "emptyObj": {} + }, + [1, "", null, {}, []] + ] +} diff --git a/test/test-suite/groups/function-removeEmpty/case016.json b/test/test-suite/groups/function-removeEmpty/case016.json new file mode 100644 index 00000000..39628d3c --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case016.json @@ -0,0 +1,8 @@ +{ + "expr": "$removeEmpty({\"empty\": \"\", \"valid\": \"data\", \"null\": null})", + "dataset": "dataset25", + "bindings": {}, + "result": { + "valid": "data" + } +} diff --git a/test/test-suite/groups/function-removeEmpty/case017.json b/test/test-suite/groups/function-removeEmpty/case017.json new file mode 100644 index 00000000..1a5a2b55 --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case017.json @@ -0,0 +1,6 @@ +{ + "expr": "[$removeEmpty({\"test\": \"value\"}, {\"invalid\": \"object\"}), $removeEmpty({\"test\": \"value\"}, [\"valid\", 123, \"another\"]), $removeEmpty({\"test\": \"value\"}, [], \"not_a_boolean\"), $removeEmpty({\"test\": \"value\"}, [], 123), $removeEmpty({\"test\": \"value\"}, [], {\"invalid\": \"object\"})]", + "dataset": "dataset25", + "bindings": {}, + "code": "T0410" +} diff --git a/test/test-suite/groups/function-removeEmpty/case018.json b/test/test-suite/groups/function-removeEmpty/case018.json new file mode 100644 index 00000000..f7c297cd --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case018.json @@ -0,0 +1,18 @@ +{ + "expr": "[$removeEmpty({\"test\": \"value\", \"empty\": \"\"}, [\"empty\"]), $removeEmpty({\"test\": \"value\", \"empty\": \"\"}, [], true), $removeEmpty({\"test\": \"value\", \"empty\": \"\"}, [], false)]", + "dataset": "dataset25", + "bindings": {}, + "result": [ + { + "test": "value", + "empty": "" + }, + { + "test": "value" + }, + { + "test": "value", + "empty": "" + } + ] +} \ No newline at end of file diff --git a/test/test-suite/groups/function-removeEmpty/case019.json b/test/test-suite/groups/function-removeEmpty/case019.json new file mode 100644 index 00000000..0ecb3328 --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case019.json @@ -0,0 +1,8 @@ +{ + "expr": "$removeEmpty({\"test\": \"value\", \"empty\": \"\"}, [], true)", + "dataset": "dataset25", + "bindings": {}, + "result": { + "test": "value" + } +} diff --git a/test/test-suite/groups/function-removeEmpty/case020.json b/test/test-suite/groups/function-removeEmpty/case020.json new file mode 100644 index 00000000..1b69efab --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case020.json @@ -0,0 +1,76 @@ +{ + "expr": "{\"cleaned_user\": $removeEmpty(user), \"original_org\": organization}", + "dataset": "dataset25", + "bindings": {}, + "result": { + "cleaned_user": { + "name": "John Doe", + "email": "john@example.com", + "profile": { + "social": { + "linkedin": "https://linkedin.com/in/johndoe" + }, + "preferences": { + "privacy": { + "public": true + } + } + }, + "projects": [ + { + "name": "Project Alpha", + "description": "A sample project", + "tags": ["work", "important"], + "metadata": { + "created": "2023-01-15" + } + } + ], + "settings": { + "theme": "dark", + "language": "en", + "features": { + "beta": false + } + } + }, + "original_org": { + "name": "ACME Corp", + "departments": [ + { + "name": "Engineering", + "employees": [ + { + "name": "Alice Smith", + "role": "Developer", + "skills": ["JavaScript", "Python"], + "certifications": [] + }, + { + "name": "", + "role": null, + "skills": [], + "certifications": null + } + ], + "budget": { + "allocated": 100000, + "spent": 0, + "remaining": null + } + }, + { + "name": "", + "employees": [], + "budget": {} + } + ], + "locations": [], + "policies": { + "remote": true, + "benefits": {}, + "training": [] + } + } + } +} diff --git a/test/test-suite/groups/function-removeEmpty/case021.json b/test/test-suite/groups/function-removeEmpty/case021.json new file mode 100644 index 00000000..e08031c5 --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case021.json @@ -0,0 +1,19 @@ +{ + "expr": "$map(organization.departments, function($dept) { $removeEmpty($dept) })", + "dataset": "dataset25", + "bindings": {}, + "result": { + "name": "Engineering", + "employees": [ + { + "name": "Alice Smith", + "role": "Developer", + "skills": ["JavaScript", "Python"] + } + ], + "budget": { + "allocated": 100000, + "spent": 0 + } + } +} diff --git a/test/test-suite/groups/function-removeEmpty/case022.json b/test/test-suite/groups/function-removeEmpty/case022.json new file mode 100644 index 00000000..4bbd4f85 --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case022.json @@ -0,0 +1,11 @@ +{ + "expr": "user.projects[name != \"\"].{\"project\": name, \"clean_metadata\": $removeEmpty(metadata)}", + "dataset": "dataset25", + "bindings": {}, + "result": { + "project": "Project Alpha", + "clean_metadata": { + "created": "2023-01-15" + } + } +} diff --git a/test/test-suite/groups/function-removeEmpty/case023.json b/test/test-suite/groups/function-removeEmpty/case023.json new file mode 100644 index 00000000..66a7d8cf --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case023.json @@ -0,0 +1,18 @@ +{ + "expr": "data.arrays.nested.{\"id\": id, \"cleaned\": $removeEmpty($)}", + "dataset": "dataset26", + "bindings": {}, + "result": [ + { + "id": 1, + "cleaned": { + "id": 1, + "value": "test" + } + }, + { + "id": null + }, + {} + ] +} diff --git a/test/test-suite/groups/function-removeEmpty/case024.json b/test/test-suite/groups/function-removeEmpty/case024.json new file mode 100644 index 00000000..3a402dd4 --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case024.json @@ -0,0 +1,27 @@ +{ + "expr": "[$removeEmpty(user.profile), $removeEmpty(user.profile, [], true)]", + "dataset": "dataset25", + "bindings": {}, + "result": [ + { + "social": { + "linkedin": "https://linkedin.com/in/johndoe" + }, + "preferences": { + "privacy": { + "public": true + } + } + }, + { + "social": { + "linkedin": "https://linkedin.com/in/johndoe" + }, + "preferences": { + "privacy": { + "public": true + } + } + } + ] +} \ No newline at end of file diff --git a/test/test-suite/groups/function-removeEmpty/case025.json b/test/test-suite/groups/function-removeEmpty/case025.json new file mode 100644 index 00000000..4bac5c1b --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case025.json @@ -0,0 +1,8 @@ +{ + "expr": "$removeEmpty(data.objects.partial)", + "dataset": "dataset26", + "bindings": {}, + "result": { + "valid": "data" + } +} diff --git a/test/test-suite/groups/function-removeEmpty/case026.json b/test/test-suite/groups/function-removeEmpty/case026.json new file mode 100644 index 00000000..9818dae5 --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case026.json @@ -0,0 +1,10 @@ +{ + "expr": "$removeEmpty(organization.departments[0].employees[0])", + "dataset": "dataset25", + "bindings": {}, + "result": { + "name": "Alice Smith", + "role": "Developer", + "skills": ["JavaScript", "Python"] + } +} diff --git a/test/test-suite/groups/function-removeEmpty/case027.json b/test/test-suite/groups/function-removeEmpty/case027.json new file mode 100644 index 00000000..90cdff44 --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case027.json @@ -0,0 +1,9 @@ +{ + "expr": "$removeEmpty(data.objects.partial, [\"empty_string\"], false)", + "dataset": "dataset26", + "bindings": {}, + "result": { + "valid": "data", + "empty_string": "" + } +} diff --git a/test/test-suite/groups/function-removeEmpty/case028.json b/test/test-suite/groups/function-removeEmpty/case028.json new file mode 100644 index 00000000..88a47d85 --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case028.json @@ -0,0 +1,10 @@ +{ + "expr": "$removeEmpty(organization.departments[0].budget, [\"remaining\"], true)", + "dataset": "dataset25", + "bindings": {}, + "result": { + "allocated": 100000, + "spent": 0, + "remaining": null + } +} diff --git a/test/test-suite/groups/function-removeEmpty/case029.json b/test/test-suite/groups/function-removeEmpty/case029.json new file mode 100644 index 00000000..f9cb1166 --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case029.json @@ -0,0 +1,11 @@ +{ + "expr": "{\"cleaned_projects\": $map(user.projects[name != \"\"], function($proj) { $removeEmpty($proj.metadata) }), \"department_count\": $count(organization.departments)}", + "dataset": "dataset25", + "bindings": {}, + "result": { + "cleaned_projects": { + "created": "2023-01-15" + }, + "department_count": 2 + } +} diff --git a/test/test-suite/groups/function-removeEmpty/case030.json b/test/test-suite/groups/function-removeEmpty/case030.json new file mode 100644 index 00000000..c875f7ca --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case030.json @@ -0,0 +1,18 @@ +{ + "expr": "organization.departments.{\"name\": name, \"clean_employees\": $map(employees[name != \"\"], function($emp) { $removeEmpty($emp) })}", + "dataset": "dataset25", + "bindings": {}, + "result": [ + { + "name": "Engineering", + "clean_employees": { + "name": "Alice Smith", + "role": "Developer", + "skills": ["JavaScript", "Python"] + } + }, + { + "name": "" + } + ] +} diff --git a/test/test-suite/groups/function-removeEmpty/case031.json b/test/test-suite/groups/function-removeEmpty/case031.json new file mode 100644 index 00000000..fbe2739f --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case031.json @@ -0,0 +1,8 @@ +{ + "expr": "data.arrays.nested[value != \"\"].{\"item_id\": id, \"cleaned_meta\": $removeEmpty(meta, [\"flags\"])}", + "dataset": "dataset26", + "bindings": {}, + "result": { + "item_id": 1 + } +} diff --git a/test/test-suite/groups/function-removeEmpty/case032.json b/test/test-suite/groups/function-removeEmpty/case032.json new file mode 100644 index 00000000..f5ab57dd --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case032.json @@ -0,0 +1,13 @@ +{ + "expr": "user.{\"profile_clean\": $removeEmpty(profile.social), \"settings_clean\": $removeEmpty(settings.features, [], true)}", + "dataset": "dataset25", + "bindings": {}, + "result": { + "profile_clean": { + "linkedin": "https://linkedin.com/in/johndoe" + }, + "settings_clean": { + "beta": false + } + } +} diff --git a/test/test-suite/groups/function-removeEmpty/case033.json b/test/test-suite/groups/function-removeEmpty/case033.json new file mode 100644 index 00000000..001aea44 --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case033.json @@ -0,0 +1,6 @@ +{ + "expr": "data.objects.nested.level1.$removeEmpty().level2.$removeEmpty()", + "dataset": "dataset26", + "bindings": {}, + "undefinedResult": true +} diff --git a/test/test-suite/groups/function-removeEmpty/case034.json b/test/test-suite/groups/function-removeEmpty/case034.json new file mode 100644 index 00000000..ecf10443 --- /dev/null +++ b/test/test-suite/groups/function-removeEmpty/case034.json @@ -0,0 +1,12 @@ +{ + "expr": "user.projects[0].{\"name\": name, \"meta\": $removeEmpty(metadata, [\"updated\"], true)}", + "dataset": "dataset25", + "bindings": {}, + "result": { + "name": "Project Alpha", + "meta": { + "created": "2023-01-15", + "updated": null + } + } +}