2

I'm building a component that allows me to compare two objects. It accepts a list of fields to compare and a list of fields that need to be ignored in string format

Here is an example of the object that will be compared:

{

// (..... More elements above .....)

taskData: {
    "uniqueId": "OrdenTrabajo48",
    "id": 48,
    "position": 1,
    "name": "Dirección Obra Civil",
    "description": "Dirección Obra Civil Afecta: Sorda, Roberto",
    "startDate": "2021-10-16T11:00:00.000Z",
    "endDate": "2022-06-01T11:00:00.000Z",
    "duration": 227,
    "progress": 73,
    "hours": 0,
    "realHours": 15,
    "predecessor": null,
    "child": [],
    "resourceInfo": [
        {
            "uniqueId": "Persona_1MJ0VE9G0",
            "id": "OrdenTrabajo48Persona_1MJ0VE9G0",
            "name": "Sorda, Roberto",
            "group": "Subgerencia de Planes y Proyectos - SUB_PLAN_PROY_SIT",
            "unit": 4.1667,
            "startDate": "2021-10-16T03:00:00.000+00:00",
            "endDate": "2022-06-01T02:59:59.000+00:00",
            "hours": 0,
            "realHours": 15,
            "avatar": "http://localhost:8091/images/llama.jpg"
        }
    ],
    "comments": null,
    "etiquetas": [],
    "baseLineStartDate": null,
    "baseLineEndDate": null
}

// (..... More elements below .....)

}

(But to clarify, it could be any object. The component is abstract and can be used anywhere)

The component doesn't know the structure of the object to compare, just the object and the paths in string format

I want to remove in every element of the array resourceInfo, the properties avatar, icon, label and color regardless the length of the array, but I don't know if there is a syntax to do that.

Also I want to remove the property realHours

This is what I tried:


const ignoredFields = [
'taskData.resourceInfo[?].avatar',    //<--- It's not working
'taskData.resourceInfo[].icon',      //<---  Neither this
'taskData.resourceInfo.label',     //<--- Or this
'taskData.resourceInfo[0].color',     //<--- This hardcode is working, but I don't know the length in that scope
'taskData.realHours' // <-- No problems here

];

const currentComparableObject = _.omit(obj, ignoredFields);
const oldComparableObject = _.omit(prev, ignoredFields);
4
  • I would add another step and use path taskData.resourceInfo and iterate that array and omit array fields ['avatar','icon','label'] etc on each object Commented Dec 24, 2021 at 21:32
  • Right, how I could make it abstract? The component that compares two objects doesn't really know the details of the object that need to compare. It just knows an object and the names of the properties that need to ignore Commented Dec 24, 2021 at 21:38
  • 1
    Then you would need to recursively keep track of paths and walk through both objects simultaneously if you don't know the structure. I'm now a bit confused since you appeared to know what paths you wanted Commented Dec 24, 2021 at 21:41
  • I know the paths that I want to compare and ignore (A parent component offers those), but I don't know the structure of the model in the abstract component Commented Dec 24, 2021 at 22:12

4 Answers 4

2
var fieldsToOmit=[];
var resourceInfoFields=['avatar','icon','label','color'];
var globalFields=['realHours'];
taskData.resourceInfo.forEach((item,index)=>{
  resourceInfoFields.forEach((field)=>{
    fieldsToOmit.push(`resourceInfo[${index}].${field}`)
  })
})
console.log( _.omit(taskData, fieldsToOmit.concat(globalFields)))
Sign up to request clarification or add additional context in comments.

1 Comment

Clever approach
2

You can remove properties in a functional manner (immutable) by using destructuring:

const {realHours, ...result} = {
    ...taskData, 
    resourceInfo: taskData.resourceInfo.map(
        ({avatar, icon, label, color, ...keep}) => keep
    )
};
console.log(result);

1 Comment

This answer is art.
1

You do not need lodash to delete fields from an array. I mean you can if you really want to but, it is trivial to loop through the array and delete the fields you want.

@Yasser CHENIK isn't wrong just doesn't do a good job of explaining the solution.

Below I have included a thorough example so you can test for yourself.

NOTE this solution mutates the original array but, it is not difficult to use this concept to make an immutable version.

const taskData = {
    "uniqueId": "OrdenTrabajo48",
    "id": 48,
    "position": 1,
    "name": "Dirección Obra Civil",
    "description": "Dirección Obra Civil Afecta: Sorda, Roberto",
    "startDate": "2021-10-16T11:00:00.000Z",
    "endDate": "2022-06-01T11:00:00.000Z",
    "duration": 227,
    "progress": 73,
    "hours": 0,
    "realHours": 15,
    "predecessor": null,
    "child": [],
    "resourceInfo": [
        {
            "uniqueId": "Persona_1MJ0VE9G0",
            "id": "OrdenTrabajo48Persona_1MJ0VE9G0",
            "name": "Sorda, Roberto",
            "group": "Subgerencia de Planes y Proyectos - SUB_PLAN_PROY_SIT",
            "unit": 4.1667,
            "startDate": "2021-10-16T03:00:00.000+00:00",
            "endDate": "2022-06-01T02:59:59.000+00:00",
            "hours": 0,
            "realHours": 15,
            "avatar": "http://localhost:8091/images/llama.jpg"
        },
        {
            "uniqueId": "Persona_1MJ0VE9G0",
            "id": "OrdenTrabajo48Persona_1MJ0VE9G0",
            "name": "Sorda, Roberto",
            "group": "Subgerencia de Planes y Proyectos - SUB_PLAN_PROY_SIT",
            "unit": 4.1667,
            "startDate": "2021-10-16T03:00:00.000+00:00",
            "endDate": "2022-06-01T02:59:59.000+00:00",
            "hours": 0,
            "realHours": 15,
            "avatar": "http://localhost:8091/images/llama.jpg"
        },
        {
            "uniqueId": "Persona_1MJ0VE9G0",
            "id": "OrdenTrabajo48Persona_1MJ0VE9G0",
            "name": "Sorda, Roberto",
            "group": "Subgerencia de Planes y Proyectos - SUB_PLAN_PROY_SIT",
            "unit": 4.1667,
            "startDate": "2021-10-16T03:00:00.000+00:00",
            "endDate": "2022-06-01T02:59:59.000+00:00",
            "hours": 0,
            "realHours": 15,
            "avatar": "http://localhost:8091/images/llama.jpg"
        },
    ],
    "comments": null,
    "etiquetas": [],
    "baseLineStartDate": null,
    "baseLineEndDate": null
}


const fieldsToOmit = [
    'avatar',
    'icon',
    'label',
    'color',
    'realHours'
]

console.log(taskData.resourceInfo);

taskData.resourceInfo.forEach(info => {
    fieldsToOmit.forEach(field => {
        delete info[field];
    })
});

console.log(taskData.resourceInfo);

2 Comments

Hi, thanks for your answer. Problem is, that the component that compares objects doesn't know the structure of the object, just the path of the fields in string format. (i.e: taskData.realHours.) lodash offers a way to navigate into an object by a path of string, but doesn't offer a way to iterate this way an array.
ah interesting. I apologize for misinterpreting the request. Good Luck!
1

Thanks for the answers to all.

To solve partially the problem, I created a function that does the following:

  • It filters the references that contains [?] (i.e: taskData.resourceInfo[?].avatar)

  • Then obtain the first part of the string (That is, the path to reach the array) and the second part (property name)

  • Using _.get from lodash it retrieves the length of the array and creates a new fieldReference with the index, so loadash can read it.

private sanitizeArrays(obj: any, fieldReferences: string[]): string[] {
        const fieldsDup = [...fieldReferences];

        // Get Elements that contains [?] in the property name
        const arrays = fieldsDup.filter(ignoredField => ignoredField.match(/\[\?]/g));

        // Remove elements that contain [?] from ignoredFieldsDuplicated
        fieldsDup.forEach((ignoredField, index) => {
            if (ignoredField.includes('[?]')) {
                fieldsDup.splice(index, 1);
            }
        });

        // Get the properties names without [?]
        const arrayPropertyName = arrays.map(ignoredField => ignoredField.split('[')[0]);
        const afterArrayPropertyName = arrays.map(ignoredField => ignoredField.split(']')[1]);

        // For each array that I have...
        arrayPropertyName.forEach((array, index) => {
            const length = _.get(obj, array).length;

            for (let i = 0; i < length; i++) {

                fieldsDup.push(array + '[' + i + ']' + afterArrayPropertyName[index]);
            }
        });

        return fieldsDup;
    }

Example input (if the object contains only one element in resourceInfo):

'taskData.resourceInfo[?].avatar',
                      'taskData.resourceInfo[?].icon',
                      'taskData.resourceInfo[?].label',
                      'taskData.resourceInfo[?].color',
                      'taskData.resourceInfo[?].fontColor',
                      'taskData.realHours'

Example output:

taskData.resourceInfo[?].icon
taskData.resourceInfo[?].color
taskData.realHours
taskData.resourceInfo[0].avatar
taskData.resourceInfo[0].icon
taskData.resourceInfo[0].label
taskData.resourceInfo[0].color
taskData.resourceInfo[0].fontColor

(javascript includes() isn't playing nice deleting the [?])

Also it doesn't work for nested arrays...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.