Javascript arrays and objects: Get differences and merge them - javascript

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)

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

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

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.

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

Related

How can I update some array entries to have multiple values?

I have an array as such:
arr = {
name: 1,
address: 1,
phone: 2,
email: 5,
};
I want to be able to add further information to this array, eg:
arr = {
name: 1 true,
address: 1 false,
phone: 2 true,
email: 5 true,
};
I've tried a few different things like:
arr.email[2] = true;
With no results (or errors).
Is there a way to do this? Or a better way of handling this issue?
I'm not entirely certain what you're going for here since you mention wanting an array ([]) but what you've shown in your question is an object ({}), but if I'm reading right you can accomplish this with an object where each key holds an array of values. That would look like:
const obj = {
name: [1],
address: [1],
phone: [2],
email: [5],
};
obj.email.push(true);
obj.email.push("whatever");
console.log(obj)
console.log(obj.email[1])
console.log(obj.email[2])
So obj is an object, but name, address, phone, and email are all arrays which you can extend as needed with array methods.
Original data structure doesn't allow you to add data the way you want. So you have to create the object with needed data structure at first. After this the original data should be moved to the new object and only after this you can add some new data.
newObj.name.push(origObj.name,true,"something1","something2");
newObj.address.push(origObj.address,false);
newObj.phone.push(origObj.phone,true);
newObj.email.push(origObj.email,true);
output
{
"name":[1,true,"something1","something2"],
"address":[1,false],
"phone":[2,true],
"email":[5,true]
}
original object
var origObj = {
name: 1,
address: 1,
phone: 2,
email: 5
};
new object
var newObj = {
name: [],
address: [],
phone:[],
email: []
};
You would need to assign a variable to your array and call a function...that way, each element could be classified (boolean, string, integer, etc.) For example,
const fruit = ['banana', 'apple', 'pear'];
console.log(fruit);

What is the best approach to clone objects properties when using map()?

I want to add a new property (contactDetails.countryName) and assign a value to a nested object stored in an array called users using the function map().
I've recently learned that I should use the spread operator (...) and then create / assign the new property in order to avoid mutating my original array of objects so I've developed 2 different implementations for this but I'm not really confident I'm following the best practices to accomplish I want to regarding the semantic and performance.
What would be the best approach to accomplish what I want to do in your opinion?
const countries = [
{ id: 3, countryName : "UK" },
{ id: 4, countryName : "Spain" },
{ id: 6, countryName : "Germany"}
];
const users = [
{ id : 1,
name: "Douglas Camp",
dateOfBirth: "23-06-1984",
contactDetails:
{
country: 3,
phone: "7373724997"
}
},
{
id : 2,
name: "Martin Stein",
dateOfBirth: "19-08-1992",
contactDetails:
{
country: 6,
phone: "3334343434"
}
},
];
const usersData = users.map(user=> {
// Version 1 : using spreading operator twice
const newUser = {
...user,
contactDetails: {
...user.contactDetails,
countryName: countries.find(c=> c.id == user.contactDetails.country).countryName
}
};
return newUser;
});
// Version 2: copying the original object property and using spread operator only for cloning the nested object properties
const newUser = {
id: user.id,
name: user.name,
dateOfBirth: user.dateOfBirth,
contactDetails: {
...user.contactDetails,
countryName: countries.find(c=> c.id == user.contactDetails.country).countryName
}
};
console.log(users);
console.log(usersData);
Here is an approach you can consider:
First of all I would Array.reduce the countries to a Map so you can get them via key/value or in this case by countries.get(key) and avoid filtering that array every time.
You can map through the users and for each one create a new object. In this case I call them accounts.
You can also consider using Object.assign
Note that both ... operator and Object.assign are shallow clone approaches. They do not recursively clone the nested objects/children. For that you can use JSON.stringify and JSON.parse etc.
let countries = [
{ id: 3, countryName : "UK" },
{ id: 4, countryName : "Spain" },
{ id: 6, countryName : "Germany"}
].reduce((r,{id, countryName}) => (r.set(id, countryName), r), new Map()) // reduce with Map
let users = [ { id : 1, name: "Douglas Camp", dateOfBirth: "23-06-1984", contactDetails: { country: 3, phone: "7373724997" } }, { id : 2, name: "Martin Stein", dateOfBirth: "19-08-1992", contactDetails: { country: 6, phone: "3334343434" } }, ];
let accounts = users.map(user => Object.assign({}, user, { // <-- map through
contactDetails: {
...user.contactDetails,
countryName: countries.get(user.contactDetails.country) // <-- get by key
}
}))
users[0].id = 2 // <-- modify users
users[0].contactDetails.phone = "00000"
console.log(users, accounts) // <-- no changes to accounts
Notice when we update the users[0].id and users[0].contactDetails.phone the accounts values did not update.
I normally use version 1, the spread operator twice. I also would consider checking out immer which allows you to do mutable updates on a cloned draft and handles merging it back for you.
const newUser = immer(user, draft => {
draft.contactDetails.countryName = countries.find(
c => c.id == user.contactDetails.country).countryName
)
})
Just edit the specific property you want and immer handles copying the rest of it.
Cloning and merging MapsSection
Just like Arrays, Maps can be cloned:
var original = new Map([
[1, 'one']
]);
var clone = new Map(original);
console.log(clone.get(1)); // one
console.log(original === clone); // false. Useful for shallow comparison
I personally like to use Version 1, as it makes your code much less redundant and easier to read. It also passes all the properties of 'user' down to newUser.

How to get JSON keys and add extra fields?

I'm trying to get the key of these json objects in order to create a new object with extra filed to create table headers in a React app. JSON data:
let example = [
{
id: 1,
city: 'New York',
},
{
id: 2,
city: 'Paris',
},
]
The function:
getKeys() {
return example.map((key) => {
return {
cityName: key, // gets the whole array
capital: false,
};
});
}
I tries Object.keys( example);, it returns integers; 0, 1.
How can I get the keys in this case? Thanks.
You are trying to map the keys for an array since example is an array. If the data are consistent throughout the array get the first element example[0] and do Object.keys().
So Object.keys(example[0])
There's no need to get the keys if you just want to add a property to the items in the array. I think there's a misunderstanding about .map, which gives you a single item/object in the array, not the keys.
Something like this perhaps?
let example = [{
id: 1,
city: 'New York',
}, {
id: 2,
city: 'Paris',
}];
const modifiedArray = function(arr) {
return arr.map(item => {
return {
id: item.id,
cityName: item.city,
capital: false,
};
})
}
const newArray = modifiedArray (example);
console.log(newArray )

JS – How to build a dynamic nested object of arrays with objects etc. from string

This is a nice evening project, but actually i'm stuck with some headache.
All I need is a function like this example:
result = set("itemCategories[0].items[0].name", "Test")
which should return:
{ itemCategories: [
{
items: [ {name: "Test"} ]
}
}]
...and in case of the given attribute "itemCategories[1].items[2].name" this result:
{ itemCategories: [
null,
{
items: [
null,
null,
{name: "Test"}
]
}
}]
Use lodash#set:
result = lodash.set({}, "itemCategories[0].items[0].name", "Test")
If you are asking about the vanilla JavaScript Set method then you could do this.
/* this is what you are trying to get.
{ itemCategories: [
{
items: [ {name: "Test"} ]
}
}]
*/
var mySet = new Set(); // your set object.
Create your data (number, text, string, object, array, null).
ver data1 = 365;
ver data2 = 'Dragonfly';
ver data3 = {name: 'Bobby', age: 20000, job: 'dj'};
Then you just add to that set using its add method.
mySet.add(data1);
mySet.add(data2);
mySet.add(data3);
So to get what you are looking for you would write this.
var itms = {items: [{name: 'test'}]};
mySet.add(itms);
The good thing about set is that is like an array. So you can use forEach.
mySet.forEach( function(val){
console.log(val); // gets all your data.
});
You can even check if a value is in your data using the has method.
mySet.has(365); // true
mySet.has(36500000); as false
JavaScript Set

How to grab a single element passed from express JSON array and output in Jade

I have an array incoming from Express to a Jade template that looks like this:
[{ id: 11,
firstname: 'asdfasdf',
lastname: 'asdfasdf',
},
{ id: 12,
firstname: 'asdfadf',
lastname: 'asdfasdf',
}]
I want to grab one of these elements and display it along with its properties on the page.
I have read several other answers here but none seem to answer my question/work properly.
I have tried,
#{data[0].id}
!{data[0].id}
#{data.id[0]}
data.id[0]
data[0].id
...and countless other combinations, how can i print this out properly?
I know my data object is being passed in correctly because I can iterate through it using a for loop elsewhere in my page. But if I only want to grab ONE element ONLY from the array, how can i achieve this?
Is this even possible without looping through the entire json array object?
UPDATE: my problem was due to the array structure being different than what I initially thought it was. My mistake, the correct way to access elements is using
#{data[0].id}
This is an array of JSON objects so you need to iterate it through
var newObject = [{ id: 11,
firstname: 'asdfasdf',
lastname: 'asdfasdf',
},
{ id: 12,
firstname: 'asdfadf',
lastname: 'asdfasdf',
}];
for (var i = 0; i < newObject.length; i++) {
var object = newObject[i];
for (var property in object) {
alert('item ' + i + ': ' + property + '=' + object[property]);
}
}
or
var newObject = "[{ id: 11,firstname: 'asdfasdf',lastname: 'asdfasdf',},{ id: 12, firstname: 'asdfadf', lastname: 'asdfasdf',}]";
var obj = eval(newObject);
alert(obj[0].id);

Categories

Resources