Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Object inside Array is verbose #204

Open
dpescatore opened this issue Jun 1, 2018 · 2 comments
Open

Remove Object inside Array is verbose #204

dpescatore opened this issue Jun 1, 2018 · 2 comments

Comments

@dpescatore
Copy link

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.

@alshakero
Copy link
Collaborator

alshakero commented Jun 2, 2018

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 😄

@ArianHamdi
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants