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)

2
  • See stackoverflow.com/help/mcve Commented Oct 19, 2017 at 14:39
  • Now your question's code is invalid ... Commented Oct 19, 2017 at 14:59

4 Answers 4

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);

Sign up to request clarification or add additional context in comments.

3 Comments

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?
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?
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.
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);
 }
});

2 Comments

What is data in you case? And kind of the same problem as in the comment to the answer of guest271314
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
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.

2 Comments

Works, until a new object is being added to the initial data.
@SVARTBERG when a new object is added, run a unionBy again.
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);

2 Comments

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?
I think you can use Object.keys , this will retrieve object keys as a array list ..

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.