Remove Object inside Array is verbose
Hello, I'm trying to remove an object from an array with a jsonpatch, but after removing I've got a lot of operations on the object performed to shift the elements of my array.
Is there another option or config param to obtain only the remove operations without shifting anything?
It should be useful if i sent JSON Patch to a server responsible of removing items and saving them to DB.
Thanks.
Hello!
This is the natural behavior of arrays and fast-json-patch accurately gives it to you. When you remove an element from an array the engine shifts every element after the removed element left by one. This behaviour is great for the vast majority of array use cases, but JSON-Patch shines a bright light on a dark side to it; this issue.
Sorry to tell you that fast-json-patch has no solution to this.
However, I have been noticing many patterns in generated JSON-Patch patches, patterns where the patch can be squashed into a smaller yet equivalent patch. And I created a library called json-squash that does this for all the patterns I hardcoded into it. Your issue inspired me to add one more pattern: array shifts and splices. You can find it here.
This is a personal project that comes without any warranties and it hasn't been used in production at all. Please use with care 😄
Hi, I tried a workaround for the situation :
let's take a look at the example below:
const initialUsers = [
{ id: 11, name: "Arian" },
{ id: 5, name: "Maxi" },
{ id: 18, name: "Nicholas" }
];
the primary key in each object is id property.
let's convert this array to an object.
I created a utility function to do so :
export const convertArrayByKeyToOBJ = (options, keyName) => {
let obj = {};
options.forEach((option) => {
const { [keyName]: key } = option;
obj[key] = option;
});
return obj;
};
const initialUsersObj = convertArrayByKeyToOBJ(initialUsers,'id');
console.log(initialUsersObj)
// {
// 11: { id: 11, name: "Arian" },
// 5: { id: 5, name: "Maxi" },
// 18: { id: 18, name: "Nicholas" }
// };
Now let's remove the second item from the users array and convert it to the object :
const editedUsers = [
{ id: 11, name: "Arian" },
{ id: 18, name: "Nicholas" }
]
const editedUsersObj = convertArrayByKeyToOBJ(editedUsers ,'id');
console.log(editedUsersObj )
// {
// 11: { id: 11, name: "Arian" },
// 18: { id: 18, name: "Nicholas" }
// };
Let's use compare function to get the difference between initialUsersObj and editedUsersObj :
import { compare } from 'fast-json-patch';
const patches = compare(initialDataObj, editedDataObj);
console.log("patches", patches);
// [
// {op : "remove" , path : '/5'}
// ]
Here we have correct patches without unnecessary operators. The pattern works well for all the operators.
To apply these patches in the backend we need to convert the initial array to an object, as well as we did before.
import { applyPatches } from 'fast-json-patch'
const initialUsersObj = convertArrayByKeyToOBJ(initialUsers,'id');
const appliedPatchObj = applyPatch(initialUsersObj , patches).newDocument;
console.log(appliedPatchObj);
// {
// 11: { id: 11, name: "Arian" },
// 5: { id: 5, name: "Maxi" },
// 18: { id: 18, name: "Nicholas" }
// };
At the final step let's convert this object to an array. there is another utility function to do so :
export const convertObjByKeyToArray = (options, keyName) => {
let array = [];
for (const property in options) {
array.push({ [keyName]: property, ...options[property] });
}
return array;
};
const appliedPatchArray = convertObjByKeyToArray(appliedPatchObj,'id')
I created a more complicated example of this pattern here.
Thanks for looking into this.