set array object from other array object - javascript

I have 2 arrays array1, array2.
array 1 objects: userName, userId
array 2 objects: userId, msg
i want to get array3: userId, userName, msg
Array1:
[{ userName: 'vimal', userId: 789 },
{ userName: 'kabilan', userId: 456 },
{ userName: 'yathavan', userId: 123 }]
Array2:
[ { userId: '123', msg: 'hi' },
{ userId: '789', msg: 'yth' },
{ userId: '789', msg: 'hu' } ]
i want to compare 2 arrays and get output like this.
Array3:
[ { userId: '123', userName: 'yathavan', msg: 'hi' },
{ userId: '789', userName: 'vimal', msg: 'yth' },
{ userId: '789', userName: 'vimal', msg: 'hu' } ]

An off-the-shelf, "functional programming" approach:
var users = [{ userName: 'vimal', userId: 789 },
{ userName: 'kabilan', userId: 456 },
{ userName: 'yathavan', userId: 123 }]
var messages = [ { userId: '123', msg: 'hi' },
{ userId: '789', msg: 'yth' },
{ userId: '789', msg: 'hu' } ]
var user_message_list = [];
messages.map(function (message) {
return users.
filter(function (user) {
return user.userId == message.userId
}).
map(function (user) {
return {
"userId": user.userId,
"userName": user.userName,
"msg": message.msg
}
})
})
.forEach(function (item) { // eliminate nested layers
user_message_list.push.apply(user_message_list, item)
})
JSFiddle Functional
Explanation:
Two arrays of objects, one a list of users, and the other a list of messages by some of those users.
You're wanting to flesh out a report of the messages showing the usernames, so start with the messages array and loop through it. Now, for each message loop through the users list and retrieve the corresponding username.
The "loopy" approach is like this:
var messages_users = []
var message_user = {}
for (ii=0; ii < messages.length; ii++) {
message_user = {
"userId": messages[ii].userId,
"msg": messages[ii].msg
}
for (iii=0; iii < users.length; iii++) {
if ( messages[ii].userId == users[iii].userId ) {
message_user.userName = users[iii].userName
}
}
messages_users.push(message_user)
}
JSFiddle Loopy
Alternatively, using Functional Programming concepts, start by maping a function to each item in the messages array. That function takes the users array and filters it to find the corresponding user object for the current message, and map on that result to combine the current message information with the filtered user result. At this point you have an object wrapped in an array, since the map and filter methods return arrays. Therefore, the final operation is to loop using the forEach method to remove the extra array layer. Some JavaScript libraries have a concatAll or better yet concatMap method that hides that extra loop. In that case you'd have something like this:
var user_message_list = messages.
concatMap(function (message) {
return users.
filter(function (user) {
return user.userId == message.userId
}).
map(function (user) {
return {
"userId": user.userId,
"userName": user.userName,
"msg": message.msg
}
})
})
The benefit here is tighter coupling between the language nomenclature and the procedural concepts. For example: filter(... vs. for (i=0; ... if ( arr[i] ===.... Both constructs loop and select items based on criteria, hence filter.
More on Functional Programming in JavaScript

I'd make a users array indexed my the userid that contains the username;
var users = [];
for(var i=0; i<arr1.length; i++)
users[arr1[i].userId] = arr1[i].userName;
now make your output array, and go through the 2nd array. using the users array to insert;
var arr3 = [];
for(var i=0; i<arr2.length; i++)
arr3.push({userId:arr2[i].userId, userName:users[arr2[i].userId], msg:arr2[i].msg});

You would do something like this, if userId value was not a String in ary2:
var ary1 =[{userName:'vimal', userId:789}, {userName:'kabilan', userId:456}, {userName:'yathavan', userId:123}];
var ary2 = [{userId:123, msg:'hi'}, {userId:789, msg:'yth'}, {userId:789, msg:'hu'}];
function specialMerge(ar1, ar2){
var r = [];
for(var i=0,l=ar1.length; i<l; i++){
var p = ar1[i];
for(var n=0,c=ar2.length; n<c; n++){
var m = ar2[n];
if(p.userId === m.userId){
r.push({userId:p.userId, userName:p.userName, msg:m.msg});
}
}
}
return r;
}
var resultArrayOfObjects = specialMerge(ary1, ary2);

Related

Javascript - Update object in array by id and move it to the first index

I have this array:
const chats = [
{ id: "chat-1", msg: { text: "World", date: (a date) } },
{ id: "chat-2", msg: { text: "Hello", date: (a date) } },
];
After receiving updates from my database, I receive this object:
// The second chat with update data
{ id: "chat-2", msg: { text: "Bye", date: (a date) } },
How can I (using ES6) replace the chat object from the original chats array and move it to the first index?
For now, I am doing this, but I am looking for a fastest way (smaller O)
// Get the modified chat
const modifiedChat = response.data;
// Search the modified chat in the chats array by id
const chatIndex = chats.findIndex(
(chat) => chat.id === modifiedChat.id
);
// Finally, using spread syntax, add the updated chat to the head of our current chats array
chats = [
modifiedChat,
...chats.slice(0, chatIndex),
...chats.slice(chatIndex + 1),
];
You can do the following,
const chats = [
{ id: "chat-1", msg: { text: "World", date: '' } },
{ id: "chat-2", msg: { text: "Hello", date: '' } },
];
const modifiedChat = { id: "chat-2", msg: { text: "Bye", date: '' } };
const newChats = [modifiedChat, ...chats.filter(item => item.id !== modifiedChat.id)];
console.log(newChats);
You can do something similar to how LRU cache works. You can now access every chat in O(1)

Count the length of messages from the same user [duplicate]

This question already has answers here:
Loop through array of objects and return sum of each total values by object element id
(4 answers)
Closed 4 years ago.
I have an array called users that includes an object, which looks like this:
'user': {
'name': name,
'msg': msg
}
It is possible that in that array a user ist listed multiple times.
I would like to count the length of all his messages (msg).
This is how I tried it:
score = [];
for (i of users) {
for (s of score) {
if (s.name === i.user.name) {
score.push({
'name': i.user.name,
'counter': i.user.msg.length
});
}
else {
s.counter = s.counter + i.user.msg.length;
}
}
}
The array score which also includes an object should only include unique users. So they shouldn't be listed twice.
Anyway, that doesn't work and I am not very happy with my code, because there must be a better and an easier solution.
You can use reduce to convert scores into a map of all the unique values.
Once you have that map, if you want you can convert that into an array.
const users = [
{
name: 'user1',
msg: 'hello',
},
{
name: 'user1',
msg: 'hello2',
},
{
name: 'user2',
msg: 'world',
},
{
name: 'user2',
msg: 'world2',
},
{
name: 'user3',
msg: 'foo',
},
];
const scores = users.reduce((scores, {name, msg}) => {
if (!scores[name]) {
scores[name] = { name, counter: 0 };
}
scores[name].counter += msg.length;
return scores;
}, {});
// Now convert to an array if you want
console.log(Object.entries(scores));
You could use object instead of array for score and use user.name as key:
const score = {};
for (const user of users) {
if (score[user.name]) {
score[user.name].counter += user.msg.length;
} else {
score[user.name] = {
name: user.name,
counter: user.msg.length,
}
}
}

How do I search one array for items from another array in MongoDB?

I have a users table that has the following data structure:
[{
userID: 0,
username: 'test0',
petsForSale: [
{ type: 'fish' },
{ type: 'cats' }
],
seekingPets: [
{ type: 'dogs' },
{ type: 'birds' }
]
},
{
userID: 1,
username: 'test1',
petsForSale: [
{ type: 'guinea pigs' },
{ type: 'dogs' },
{ type: 'hamsters' }
],
seekingPets: [
{ type: 'ferrets' }
]
}]
I'm trying to execute a GET that returns matched users based on petsForSale and seekingPets. For example, if a user is selling dogs, they will show up on the list of matched results for any user with dogs in seekingPets. I'm very close, here's my router code so far:
router.get('/:id/findmatches', function(req, res) {
var db = req.db;
var collection = db.get('users');
var uid = req.params.id;
//var seeking_pets = collection.find({ userID: uid }, { seekingPets: 1 });
//var seeking_pets = collection.find({ userID: uid }, { seekingPets: { type: 1 }});
var seeking_pets = [ 'dogs', 'birds' ]; // *Hard-coded is the only way I can get it to work
collection.find({ petsForSale: { $elemMatch: { type: { $in: seeking_pets }}}}, function(e, docs) {
res.json(docs);
});
});
This code compiles and works just fine with seeking_pets hard-coded - visiting /users/0/findmatches returns user test1 as expected. I'm stuck extracting the list of seekingPets from the userID in the request and searching through it in collection.find. The two commented lines are what I've tried without success. I've also tried converting the collection to an array.
As find is an async method, you must implement a callback in order to get the values returned from the database. Also notice that your hardcoded values are strings, but the seekingPets array is of objects.
Try this:
router.get('/:id/findmatches', function(req, res) {
var db = req.db;
var collection = db.get('users');
var uid = req.params.id;
collection.findOne({ userID: uid }, function(err, user)
var seeking_pets = [].
for (var i = 0; i < user.seekingPets.length; i++) {
seeking_pets.push(user.seekingPets[i].type);
}
collection.find({ petsForSale: { $elemMatch: { type: { $in: seeking_pets }}}}, function(e, docs) {
res.json(docs);
});
});
});
Notice that I use findOne in order to get an object instead of an array, and from this object I get the seekingPets array. Of course you can improve this code, eliminate some dispensable variables like seeking_pets and handle the database error if the id is not found.

Array not added to object in array

I want to combine two arrays from MongoDB: an array of contact objects and an array of appointments.
My current solution is to query all contacts and appointments, loop over each array, compare the email and if they match, add each appointment to its corresponding contact.
var contacts, appointments;
Contact.find(query, function(err, result1) {
if (err) {
console.log(err)
}
else {
contacts = result1;
Appointment.find({isArchived : false}, function(err, result2) {
if (err) {
console.log(err);
}
else {
appointments = result2;
for (var i=0; i<contacts.length;i++) {
contacts[i]["appointments"] = [];
for (var j=0; j<appointments.length;j++) {
if (contacts[i].email === appointments[j].owner) {
contacts[i].appointments.push(appointments[j]);
}
}
}
res.send(contacts);
}
});
}
});
Contacts:
[{
email: "example1#example1.com"
}, {
email: "example2#example2.com"
}]
[{
email: "example1#example1.com",
start: "2015-01-01",
end: "2015-02-01"
}, {
email: "example1#example1.com",
start: "2015-02-01",
end: "2015-03-01"
}]
// Desired output
[{
email: "example1#example1.com",
appointments: [{
email: "example1#example1.com",
start: "2015-01-01",
end: "2015-02-01"
}, {
email: "example1#example1.com",
start: "2015-02-01",
end: "2015-03-01"
}]
}, {
email: "example2#example2.com",
appointments: []
}]
I know it's a mess, and I'm also unable to append the appointment array to each contact. I can only assign values to existing object keys.
So it's really two questions: 1) Why can't I append the array to an existing contact, 2) what is a more efficient solution?
Apparently you cannot extend Mongoose results directly. For that to work, you must call .toObject() on the object you wish to extend.
I would love to have suggestions for a cleaner solution, preferrably one that only involves one mongodb call, and no manual array operations.

Ordering in javascript

How easy/hard is it to order collections in javascript (alphabetical and numerically).
Say I have a collection like:
var map = {
user: { id: "23434", username: "mrblah" },
user: { id: "1010", username: "johnskeet" }
};
And I want to order the collection by id and username.
Update
correction thanks:
var map = [ { id: "23434", username: "mrblah" }, { id: "1010", username: "johnskeet" } ];
var map = {
users: [
{ id: "23434", username: "mrblah" },
{ id: "1010", username: "johnskeet" }
]
};
map.users.sort(function(a, b) {
return a.id - b.id;
});
map.users.sort(function(a, b) {
return a.username.localeCompare(b.username);
});
You want your object to be an array:
var map = [
{ id: "23434", username: "mrblah" },
{ id: "1010", username: "johnskeet" },
{ id: "1220", username: "alohaguy" }
];
We need a utility to display the usernames in order so we can test our work:
var displayUsernames = function(map) {
var out = [];
for (var i=0;i<map.length;i++) {
out.push((map[i].username));
}
alert(out.join(', '));
};
If we use it: displayUsernames(map); we get mrblah, johnskeet, alohaguy
Since it's an array, so we can use .sort(), like this: map.sort();, but if we do that we still get:
mrblah, johnskeet, alohaguy
...from displayUsernames(map); because the array of objects can't be sorted the same way as if it were an array of numbers or strings.
However, if we pass the sort() function a comparison function...
var myCompareFunction = function(a, b) {
return a.username.localeCompare(b.username);
};
Then pass it into map.sort()
map.sort(myCompareFunction);
Now, when we display it again with displayUsernames(map); we get alohaguy, johnskeet, mrblah
Hope that helps.

Categories

Resources