How to check two objects for difference in values in JS? - javascript

I want to edit a existing user in Node. My front-end is in pug.
My user table has lots of fields (about 20 to 25), and some have to be unique, like email and username, which I have functions to check if they are not a duplicate.
I only want to update values that has changed on the client, my edit form already has the values of the user of course.
I thought the best way to achieve this is to check all the inputs from req.body, and if it is different from any user values, I should update it. (Perhaps any different methods? Can't I check if the inputs are 'dirty'?)
This could be the situation. Note the req.body object, with values that my user table doesn't have, like password_confirm
req.body = {
username: 'test',
email: 'user#user.com',
password: '1234',
password_confirm: '1234',
location: 'New York',
website: 'new-website.com',
bio: undefined,
expertise: 'New expertise'
}
user = {
username: 'test',
email: 'user#user.com',
password: '1234',
location: 'San Fransico',
website: 'website.com',
bio: null,
expertise: null
}
I now only want to update the changed location, website and expertise fields. I tried many things, using reduce and lodash, but I can't get the fields that I'm looking for.
NB
I already checked different StackOverflow questions but nothing seems to work for my situation..

From what I understood from your question, give this a try,
Object.keys(req.body).forEach((key)=>{
if(user[key] && user[key]!=req.body[key]){
user[key] = req.body[key];
}
})

Well, I think you are over complicating. You don't even need lodash for this.
Object.assign({}, user, req.body);
would work, since you said yourself that you can have different fields in req.body.

If you need diff object use this:
function diff(oldObject, newObject) {
const diff = {};
Object.keys(oldObject).forEach((key) => {
if (oldObject[key] != newObject[key] && newObject[key] !== undefined) {
diff[key] = newObject[key];
}
});
return diff;
}
var body = {
username : 'test',
email : 'user#user.com',
password : '1234',
password_confirm : '1234',
location : 'New York',
website : 'new-website.com',
bio : undefined,
expertise : 'New expertise'
}
var user = {
username : 'test',
email : 'user#user.com',
password : '1234',
location : 'San Fransico',
website : 'website.com',
bio : null,
expertise : null
}
function diff(oldObject, newObject) {
const diff = {};
Object.keys(oldObject).forEach((key) => {
if (oldObject[key] != newObject[key] && newObject[key] !== undefined) {
diff[key] = newObject[key];
}
});
return diff;
}
console.log(diff(user, body));

I figured it out.
Based on #ponary-kostek & #anuragasaurus 's answer, this is what worked for me:
const differences = {};
Object.keys(req.body).forEach((key)=> {
if(req.body[key].length == 0) req.body[key] = null;
if(stakeholder.hasOwnProperty(key) && stakeholder[key]!=req.body[key]){
differences[key] = req.body[key];
}
});
This returns an object with the changed keys and their values.
Because the fields from my user object are retrieved from a SQL DB, they are null. Also, empty body fields are not undefined, but strings. Meaning if the input is empty, it is an empty string, and to compare it to my user object it should first be null.
Thanks everyone for their answers.

Related

Simplify nested for loop

I have this code:
let peopleInRoom = [];
for (let message of messages) {
for (let email of message.user.email) {
if (!peopleInRoom.includes(email)) {
peopleInRoom.push(email);
}
}
}
let peopleInRoomElement = peopleInRoom.map(person => (
<li>{person}</li>
))
Basically I am trying to get all the unique emails and display them.
Is there a shorter and more efficient way (maybe some ES6 features) to write the same code? Seems too much code than needed.
I looked at this answer: How to get distinct values from an array of objects in JavaScript?
EDIT: Above code does not do what I want.
My data looks like this:
[
{
text: 'foo',
user: { email: 'foo#bar.com', password: 'foo' }
},
{
text: 'baz',
user: { email: 'baz#qux.com', password: 'baz' }
}
]
The objects are all messages. And I want to get an array of all the unique emails from each message
You can use the Set object that is built into JavaScript. Set object actually keep the distinct primitive values.
const messages = [
{
text: 'foo',
user: { email: 'foo#bar.com', password: 'foo' }
},
{
text: 'baz',
user: { email: 'baz#qux.com', password: 'baz' }
}
]
const peopleInRoom = [...new Set(messages.map(message => message.user.email))];
It actually extracts the email from each message and then passes it to the Set object which only keeps the unique set of emails. After that, it will spread that Set to the array, since Set is also an iterable and returns the array of the people in room.
If I understand correctly, people have messages, messages have email addresses and the OP seeks the unique set of email addresses. If that's all the data available, then there's no alternative but to iterate it, checking to see if each email has been collected already, and collecting it if it hasn't been.
There are ways to conceal this work by doing it in library code. Probably the highest level utility is lodash's _.uniqueBy, but the work must be done one way or another.
The Set object enforces uniqueness of its elements. You can use it this way:
const peopleInRoom = Array.from(new Set(messages.map(message => message.user.email)));
First you can make an array of all the email addresses:
const data = [
{
text: 'foo',
user: { email: 'foo#bar.com', password: 'foo' }
},
{
text: 'baz',
user: { email: 'baz#qux.com', password: 'baz' }
}
]
const emailArray = data.map((elem) => {
return elem.user.email;
}
and then you can filter them to be unique:
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
emailArrayFiltered = emailArray.filter(onlyUnique);
see here for ref link
What about:
const peopleInRoom = messages.map(e => e.user.email && e.user.email);
console.log(peopleInRoom)
gives you this output:
["foo#bar.com", "baz#qux.com"]

Returning property "name" inside an object without know the "name"

Alright, this is probably a really stupid question, but I must ask. I have an object inside an object:
var user = {
playlist: "abcd1234",
role: {
basic: true,
}
};
I know I can access the role by:
user.role
which will return {basic: true} in the console. My question is how could I return just "basic" if I didn't know what was inside role?
I am working on a project where I am getting multiple objects (of users) and need to put their roles into a select field. I was hoping just to get "basic" (for example) to look a little nicer as an option for the user to select rather than basic = true.
If all you want is the name of the property inside user.role, Object.keys() is probably what you want. It returns in an array all of an object's property names (also known as "keys").
var user = {
playlist: "abcd1234",
role: {
basic: true,
}
};
let roles = Object.keys(user.role);
console.log(roles); // ["basic"]
But if you want only the roles that are true, that requires some filtering:
var user = {
playlist: "abcd1234",
role: {
basic: true,
admin: false,
free: true
}
};
const reduceTrues = (acc, [key, val]) => {
if (val === true) { acc.push(key); }
return acc;
}
const trueRoles = Object.entries(user.role).reduce(reduceTrues, []);
console.log(trueRoles);
// [ 'basic', 'free' ]
Use Object.keys() method like this
var user = {
playlist: "abcd1234",
role: {
basic: true,
}
}
const role = Object.keys(user.role)
console.log(role) // ["basic"]
console.log(user.role[role]) // true
Full documentation in MDN here
If you want to get the properties of an object you can simple use
var user = {
playlist: "abcd1234",
role: {
basic: true,
}
};
var roles=Object.keys(user.role);
console.log(roles);
<html>
<script>
var user = {
playlist: "abcd1234",
role: {
basic: true,
admin: false,
free: false
}
};
for(role in user.role){
if(user.role[role]){
console.log(role);
}
}
</script>
</html>
So you are essentially iterating over unknown properties in the user.role object. For every role in user.role, check if it is true with user.role[role] - the [role] is the variable name we are not aware of - then i just console.log if it is true, you can run other logic if you wish.
If you have several roles, I suggest you make the roles as a comma separated list and then split using the String.split() method.
For example:
var user = { playlist: "abcd1234", role: "basic, pro, other" };
var roles = user.role.split(",");
//You now have an array of roles
But in case you have just 1 role, simply set it as a string as in
var user = { playlist: "abcd1234", role: "basic" };
//You can check if role is empty by a simple if statement
if(user.role != null && user.role != undefined && user.role != "")
console.log("user role is "+user.role);

TypeError: Cannot read property '0' of undefined from the Array using AngularJS

I am trying to create a webpage where when a user logs in from the login page, my page that is the 'user management' page should display users belonging to the login user's company.
For eg: When a user belonging to company APPLE logs in, this webpage should only display users belonging to APPLE.
In the code, "currentUser" is the login user I get from the login page via localStorage getItem. "allUsers" array is the list of all users from different companies. Empty array "user" should display only display Current user's company's-users from the list of allUsers.
For that I have used FOR LOOP seen at the end of the JS file. I'm getting a error that the property 'company' is undefined. Looking for someone to help me. Thanks in advance.
var myApp = angular.module("myApp", []);
myApp.controller("myController", function ($scope) {
console.log("in controller...");
$scope.newUser = {};
$scope.info = "";
if ($scope.users = JSON.parse(localStorage.getItem("users")) !== null) {
$scope.users = JSON.parse(localStorage.getItem("users"));
}
if (localStorage.getItem("currentUser") !== null) {
var currentUser = JSON.parse(localStorage.getItem("currentUser"));
console.log("Received");
}
else {
console.log("Not received");
}
if (localStorage.getItem("allUsers") === null) {
$scope.allUsers = [
{ email: "John#yahoo.com", password:"John123", firstName: "John", lastName: "Doe", contact: "281-283-2480", role: "Supplier-Admin", company: "Apple" },
{ email: "Rick#yahoo.com", password: "Rick123", firstName: "Rick", lastName: "Fraiser", contact: "987-283-2489", role: "Supplier-User", company: "Apple" },
{ email: "Sam#yahoo.com", password: "Sam123", firstName: "Sam", lastName: "Tarly", contact: "456-786-2480", role: "BuyerAdmin", company: "Samsung" }
];
localStorage.setItem("allUsers", JSON.stringify($scope.allUsers));
// localStorage.setItem("users", JSON.stringify($scope.users));
} else {
$scope.allUsers = JSON.parse(localStorage.getItem("allUsers"));
}
//filter allUsers based on currentUser's role
for (var i = 0; $scope.allUsers.length; i++) {
$scope.users = [{}];
if ($scope.allUsers[i].company === currentUser[0].company) {
$scope.users.push($scope.allUser[i]);
}
localStorage.setItem("users", JSON.stringify($scope.users));
}
});
localStorage.getItem() returns string representation of null, if the key has null value in localStorage so your null check might not be behaving like expected. I'd recommend to wrap getItem in a function that would check that.
function(itemName) {
const item = localStorage.getItem(itemName);
return item === 'null'
? null
: item;
}
Your problem is probably in the place where you set currentUser, maybe because of the same null check.
Also try declaring your i variable in for loop.
for(var i = 0; ...) ...
Edit
There seems to be an error in this line
if ($scope.allUsers[i].company === currentUser[0].company) {
$scope.users.push($scope.allUser[i]);
}
You're trying to push $scope.allUser[I] which is not defined, you've probably meant to write $scope.allUsers[I].

How do i tell Mongoose NOT to save a field?

How do i tell Mongoose not to save the age field if it's null or undefined?
Or could i do this in Express somehow?
Express
router.put('/edit/:id', function(req, res) {
Person.findOneAndUpdate({ _id: req.params.id }, {
name: req.body.updateData.name,
age: req.body.updateData.age
}, { new: true });
})
Mongoose Schema
var PersonSchema = new Schema({
name: { type: String},
age: {type: String}
})
An explination (if u ask why i need this)
I'm using the same html template for new person and edit person. When i create a new person, Mongoose will save just the name field if i leave the age field empty. But when i use the edit template, Mongoose will always set the age field as null, even if the field is empty. I can't think of anything to stop this.
You could manage your request data before the db update, like:
router.put('/edit/:id', function(req, res) {
let update = {name: req.body.updateData.name};
if (req.body.updateData.age != "") {
update.age = req.body.updateData.age;
}
Person.findOneAndUpdate({ _id: req.params.id }, update, { new: true });
})
#Aioros, you answer gave me an idea, however i found this solution to match my issue. I'm just deleteing the null or undefined object elements before they are send to Mongoose.
let update = {
name: req.body.updateData.name,
age: req.body.updateData.age
};
if (update.age === null || update.age === undefined) delete update.age
Person.findOneAndUpdate({ _id: req.params.id }, update, { new: true });

Check if property is not undefined

I'm building a node+express app and I'm filling an object with JSON that's submitted from a form in the frontend. This works, unless I leave a field empty in the form so that e.g. req.body.address.street is empty/undefined.
This will result in the error:
TypeError: Cannot read property 'street' of undefined
var b = new Business({
name: req.body.name,
phone: req.body.phone,
address: {
street: req.body.address.street,
postalCode: req.body.address.postalCode,
city: req.body.address.city
},
owner: {
email: req.body.owner.email,
password: req.body.owner.password
}
});
My question is how I can best prevent my app from crashing when values are empty. I would like to avoid manually checking each and every property in my app against undefined.
I'm wondering what the best practice is for this common issue.
I don't know if you use jQuery in your project, but if you do, you can create a mask:
// creating your object mask
var req = {
body: {
name: '',
phone: '',
address: {
street: '',
postalCode: '',
city: ''
},
owner: {
email: '',
password: ''
}
}
}
And then, you simply use the jQuery "extend" method (req2 is your submmited object):
$.extend(true, req, req2);
I've create this fiddle for you!
-
Update
Nothing related to your question, but I've just noticed that you're passing an object with a similar structure of req.body to the Business class. However, there is no need to copy property by property manually - you can make, for example, a simple copy of req.body to pass as parameter:
var b = new Business($.extend({}, req.body));
or
var b = new Business(JSON.parse(JSON.stringify(req.body)));
You can't, really. You have two options;
Use a try/ catch:
try {
var b = new Business({
//
});
} catch (e) {
// Something wasn't provided.
}
... or you can define a helper function:
function get(path, obj) {
path = path.split('.');
path.shift(); // Remove "req".
while (path.length && obj.hasOwnProperty(path[0])) {
obj = obj[path.shift()];
}
return !path.length ? obj : null;
}
... you could then replace your use of req.body.address.street etc. with get('req.body.address.street', req).
See a demo here; http://jsfiddle.net/W8YaB/

Categories