1

So I have this scenario where I have a client-app which sends data (array of objects) to a server which then forwards the data to other clients connected to this server.

In the client-app, the data is constantly changing, meaning: Values change, new objects inside the array pop up, objects being removed, and so on ...

Now I want the other clients to always receive the latest data. And because I dont want the client-app to just push the whole new data to the server which then forwards the whole new data to the other clients, I decided to let the client-app only push the changes (using this library: https://www.npmjs.com/package/deep-object-diff).

The other clients then receive an array of objects with only the data that has actually changed and because they know the previous data array, I want them to "merge" the array of changes with the old data object.

My actual problem is the merging. I dont know how to properly do this. Especially if I have an array of objects without any key for the objects.

So my data looks something like this:

let data = [
  {
    name: 'Peter',
    age: 26,
    sID: 546589995544
  },
  {
    name: 'John',
    age: 33,
    sID: 554589525469
  }
];

Actually there's much more but well, thats the structure.

So if the diff library says, this are the changes:

let changes = {
  {
    age: 34,
    sID: 554589525469
  }
};

(notice that I now have an object of objects, not an array of objects. Thats what the diff-library returns)

I want the merged object to be

[
  {
    name: 'Peter',
    age: 26,
    sID: 546589995544
  },
  {
    name: 'John',
    age: 34,
    sID: 554589525469
  }
];

(John is now one year older)

So I totally believe that this would be much easier if I had a key to the objects as an identifier, but still I think there has to be a solution for exactly this scenario. And as you can see, the sID property could act as an identifier, its just not a key.

I would apprectiate if someone could point out how to do it in both cases (with and without a specific key for the objects)

SVARTBERG
  • 435
  • 1
  • 7
  • 16

4 Answers4

1

You could use a sId Map for fast lookup:

const byId = new Map( data.map( el => [el.sID, el]));

Then for every change we can find if the obj already exists, if not we add it, if yes we mutate:

changes.forEach(change => {
 const res = byId.get( change.sID );
 if( res ){
   Object.assign( res, change);
 }else{
   data.push(change);
   byId.set( change.sID, change);
 }
});
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • What is data in you case? And kind of the same problem as in the comment to the answer of guest271314 – SVARTBERG Oct 19 '17 at 14:43
  • Oh I'm sorry, didnt get the fact that your code is overriding the old data object. Thanks so far. Gotta do some more tests – SVARTBERG Oct 19 '17 at 14:46
1

You can use .find() to find the object within the array where values should be changed, Object.assign() to set the values

let data = [{
    name: 'Peter',
    age: 26,
    sID: 546589995544
  },
  {
    name: 'John',
    age: 33,
    sID: 554589525469
  }
];

let changes = [{
  age: 34,
  sID: 554589525469
}];

for (let prop of changes) {
  let {sID} = prop;
  Object.assign(data.find(({sID: id}) => id === sID), prop)
}

console.log(data);
guest271314
  • 1
  • 15
  • 104
  • 177
  • Seems like a nice approach which kinda works. It just doesnt work for me because the diff-library does not return an array of objects but an object with objects as members. And then, you solutions doesn't work anymore, because changes[Symbol.iterator] is not a function. Any fix to this? – SVARTBERG Oct 19 '17 at 14:28
  • 1
    You can use `Object.values()` or `Object.entries()` to convert an object to an array of arrays of property, value pairs. Why did you not include the actual object used at code at Question? – guest271314 Oct 19 '17 at 14:30
  • My fault, sry. Didn't think about it while writing the question. So you think I should convert the changes to be an array of objects again? I gotta look into how to achieve that with your suggested functions. Thanks. – SVARTBERG Oct 19 '17 at 14:45
1

Using lodash, you can accomplish this with unionBy :

const newData = _.unionBy(changes, data, 'sID'); // values from changes will be picked

This will pick objects from both the arrays based on sID and combine them into a single array.

Fawaz
  • 3,404
  • 3
  • 17
  • 22
1

If your changes data is object of objects , you can use Object.values to loop data value and merge same id data by Object.assign

let data = [
  {
    name: 'Peter',
    age: 26,
    sID: 546589995544
  },
  {
    name: 'John',
    age: 33,
    sID: 554589525469
  }
];

let changes = {
    0:
  {
    age: 34,
    sID: 554589525469
  }
};

data.filter((idx,i)=>
    Object.values(changes).forEach((index)=>
        (index.sID == idx.sID) ? Object.assign(data[i],index) : null
        )
);
console.log(data);
Jack jdeoel
  • 4,554
  • 5
  • 26
  • 52
  • Thanks for your answer. Works so far. Can you enlighten me on how it would work if I had and object of objects with keys for each object and not just a new field with a unique identifier? – SVARTBERG Oct 20 '17 at 06:26
  • I think you can use `Object.keys` , this will retrieve object keys as a array list .. – Jack jdeoel Oct 20 '17 at 06:32