Removing Empty Collections Not Working on Specific Field

I’m using the provided script in the help docs to remove empty objects and arrays however it does not remove a specific custom field type that uses text box list items are empty.
This is the script I am using

If the field value is empty for the fisrt custom field below the script removes it but if the field value is empty for a text box list type of custom field then it only removes the field_vaue leaving behind the id

Any help would be apprecicated

{
    "customFields": [
        {
            "id": "4zhHJUa2JKaoyFVlMNjt",
            "field_value": "555"
        },
        {
            "id": "U90xG1jeIeOf40RFgoWV",
            "field_value": {
                "960b5c91-e79e-44f4-a0a6-4d18b5bedaa8": "User",
                "c757062f-c6fb-473c-b65d-f1293278a2b2": "Test"
            }
        }
    ]
}

output when fields are empty. The text box list item leaves the id

{
    "customFields": [
      
        {
            "id": "U90xG1jeIeOf40RFgoWV",
           
        }
    ]
}

Script

function removeEmpty(input) {
 if (typeof input !== "object" || input === null) return undefined;
 return Object.entries(input).reduce((acc, [key, value]) => {
  if (Array.isArray(value)) {
    acc[key] = value.map(item => removeEmpty(item));
    return acc;
  }
  if (typeof value === "object" && value !== null && !(value instanceof Date)) {
    if (Object.keys(value).length === 0) return acc;
    acc[key] = removeEmpty(value);
    return acc;
  }
  if (value === null) return acc; // comment this line if you have to pass null values.
  acc[key] = value;
  return acc;
 }, {});
}

Not sure, what exactly you want to do with the script and how your overall datastructure looks like, but assuming that,

If the Input is,

{
    "customFields": [
        {
            "id": "4zhHJUa2JKaoyFVlMNjt"
        },
        {
            "id": "U90xG1jeIeOf40RFgoWV",
            "field_value": {
                "960b5c91-e79e-44f4-a0a6-4d18b5bedaa8": "User",
                "c757062f-c6fb-473c-b65d-f1293278a2b2": "Test"
            }
        }
    ]
};

And, you want the output to be,

[{
  field_value: {
    960b5c91-e79e-44f4-a0a6-4d18b5bedaa8: "User",
    c757062f-c6fb-473c-b65d-f1293278a2b2: "Test"
  },
  id: "U90xG1jeIeOf40RFgoWV"
}]

What you want to do is, the script that you are working with will only work with a collection not an array, it does work with array as well but the problem is it will iterate over individual keys and if the value is empty it will remove that from your data.

So, You can rewrite the script for this particular, usecase to look like this,

function removeEmpty(input) {
 if (typeof input !== "object" || input === null) return undefined;
  return input.customFields.filter(function( obj ) {
    return obj.field_value !=null;
});
}

You can check the output here.

1 Like

Thanks nah just wating to remove empty ones and if the field_value was empty on the picklist items it was leaving behind the id still and then the API was seeing it as an empty string and wiping out the existing data ont he crm

If that is the case, you can use the script I shared, haven;t tried it through Make’s IML but since it is a straightforward JS code, it should run fine with it.

1 Like

Having to use this big long one or it breaks if someone enter null or erase in a field

function removeEmpty(input) {
  if (typeof input !== "object" || input === null) return undefined;
  
  if (Array.isArray(input)) {
    return input.filter(item => {
      const cleanedItem = removeEmpty(item);
      return !(typeof cleanedItem === "object" && Object.keys(cleanedItem).length === 0);
    }).map(item => removeEmpty(item));
  }

  if (input === "{{erase}}" || input === "{{null}}") {
    return undefined;
  }
  
  const nonCustomFields = Object.entries(input).filter(([key]) => key !== "customFields");
  const customFields = input.customFields || [];

  const cleanedNonCustomFields = nonCustomFields.reduce((acc, [key, value]) => {
    if (typeof value === "object" && value !== null && !(value instanceof Date)) {
      const cleanedValue = removeEmpty(value);
      if (Object.keys(cleanedValue).length === 0) return acc;
      acc[key] = cleanedValue;
      return acc;
    }
    if (value === null || value === "{{erase}}" || value === "{{null}}") return acc;
    acc[key] = value;
    return acc;
  }, {});

  const cleanedCustomFields = customFields.filter(customField => {
    if (typeof customField.field_value === "object" && customField.field_value !== null) {
      const cleanedFieldValue = removeEmpty(customField.field_value);
      if (Object.keys(cleanedFieldValue).length === 0) return false;
      customField.field_value = cleanedFieldValue;
    }
    return true;
  });

  return {
    ...cleanedNonCustomFields,
    ...(cleanedCustomFields.length > 0 && { customFields: cleanedCustomFields }),
  };
}

Basically wanting to remove anything empty and not just the custom fields. But I still have not figured out how to clear a custom field on the CRM as if you now try to pass an empty string it removes the whole field from passing which I thought would clear the field in the CRM

Hi @scott74,

For this, What you can do is, add the function that I shared as a new IML function and once it is setup, you can pass the cleanedCustomFields to the newly created function, which should resolve the issue you are facing.

1 Like

@Runcorn Not sure I follow., The script you shared did not seem to be removing the id. Here is a quick video to show what is happening.

Here are the two current scripts
Here is how I am calling them

"body": "{{removeEmptyTextBoxList(removeEmpty(temp.body))}}",

removeEmpty

function removeEmpty(input) {
  if (typeof input !== "object" || input === null) return undefined;

  if (Array.isArray(input)) {
    return input
      .filter(item => {
        const cleanedItem = removeEmpty(item);
        return !(typeof cleanedItem === "object" && Object.keys(cleanedItem).length === 0);
      })
      .map(item => removeEmpty(item));
  }

  const nonCustomFields = Object.entries(input).filter(([key]) => key !== "customField");
  const customField = input.customField || {};

  const cleanedNonCustomFields = nonCustomFields.reduce((acc, [key, value]) => {
    if (typeof value === "object" && value !== null && !(value instanceof Date)) {
      const cleanedValue = removeEmpty(value);
      if (Object.keys(cleanedValue).length === 0) return acc;
      acc[key] = cleanedValue;
      return acc;
    }
    if (value === null) return acc;
    acc[key] = value;
    return acc;
  }, {});

  const cleanedCustomField = Object.entries(customField).reduce((acc, [key, value]) => {
    if (typeof value === "object" && value !== null && !(value instanceof Date)) {
      const cleanedValue = removeEmpty(value);
      if (Object.keys(cleanedValue).length === 0) return acc;
      acc[key] = cleanedValue;
      return acc;
    }
    if (value === null) return acc;
    acc[key] = value;
    return acc;
  }, {});

  const cleanedTags = Array.isArray(input.tags) && input.tags.length > 0 ? input.tags : undefined;

  const result = Object.assign({}, cleanedNonCustomFields);

  if (Object.keys(cleanedCustomField).length > 0) {
    result.customField = cleanedCustomField;
  }

  if (cleanedTags) {
    result.tags = cleanedTags;
  }

  return result;
}

removeEmptyTextBoxList

function removeEmptyTextBoxList(input) {
  if (!input || !input.customField) {
    return input;
  }

  const cleanedCustomField = {};

  for (const [key, value] of Object.entries(input.customField)) {
    if (isTextBoxList(value)) {
      const cleanedValue = {};

      for (const [fieldKey, fieldValue] of Object.entries(value)) {
        if (fieldValue !== null) {
          if (typeof fieldValue === "object" && Object.keys(fieldValue).length === 0) {
            continue;
          }

          if (typeof fieldValue === "string" && (fieldValue.trim() === "" || fieldValue.trim() === "{{erase}}")) {
            continue;
          }

          cleanedValue[fieldKey] = fieldValue;
        }
      }

      if (Object.keys(cleanedValue).length > 0) {
        cleanedCustomField[key] = cleanedValue;
      }
    } else {
      cleanedCustomField[key] = value;
    }
  }

  const result = Object.assign({}, input, { customField: cleanedCustomField });

  return result;
}

Thanks, Scott