Map dynamically array to nested object - javascript

I want to retrieve the form content on a submit and map that data into an object.
let userData = $(e.currentTarget).serializeArray();
let userDataObject = this.serializedToObject(userData);
--
Template objects to send through POST
serializedToObject(serializedArray) {
let templateObject = {
privider: '',
pop3: {
host: '',
port: 110,
ssl: false
},
imap: {
host: '',
port: 993
},
email: '',
password: ''
};
for (let data in serializedArray) {
}
return templateObject;
}
--
The form of the userData is
[Object, Object, Object, Object, Object, Object, Object]
-- While on object is of form
Object: {
name: 'provider',
value: 'Aladin'
}
Object: {
name: 'imap-host',
value: '955'
}
Object: {
name: 'imap-port',
value:
}
Object: {
email: 'test#gmail.com',
value:
}
So I need some help to map that array of objects to the templateObject.
AnyHelp will be highly appreciated.
Update
[{"name":"name","value":"Nicholas Barbaros"},{"name":"email","value":"george#google.com"},{"name":"password","value":"nicu121-mujik"},{"name":"imap","value":"imap.server.com"},{"name":"imap-port","value":"ad"},{"name":"pop3-host","value":"pop.server.com"},{"name":"pop3-port","value":"465"}, {"name":"pop3-ssl","value":"false"}]

The name-property of each serializedArray's object has a minus-character (-) representing nested objects, you can split by those characters and then set the values of your templateObject's properties:
var serializedArray = [{"name":"name","value":"Nicholas Barbaros"},{"name":"email","value":"george#google.com"},{"name":"password","value":"nicu121-mujik"},{"name":"imap","value":"imap.server.com"},{"name":"imap-port","value":"ad"},{"name":"pop3-host","value":"pop.server.com"},{"name":"pop3-port","value":"465"}, {"name":"pop3-ssl","value":"false"}];
// your defaults
var templateObject = {
provider: '',
pop3: {
host: '',
port: 110,
ssl: false
},
imap: {
host: '',
port: 993
},
email: '',
password: '',
};
serializedArray.forEach(function(obj) {
var deep = obj.name.split('-');
var key = deep.pop();
var level = templateObject;
deep.forEach(function(inner) {
var nested = level[inner];
var isObject = Object(nested) === nested;
level = isObject ? nested : {};
});
level[key] = obj.value;
});
// return templateObject; // when inside your function
console.log(templateObject);

You can also use reduce method, in a more functional way
const array = [{
name: 'provider',
value: 'Aladin'
},{
name: 'imap-host',
value: '955'
},{
name: 'imap-port',
value: 'something'
}];
const newObject = array.reduce( (acc, item, {}) => {
acc[item.name] = item.value;
return acc;
})
console.log(newObject);
Link to fiddle: https://jsfiddle.net/y5xkddnn/

There is too much overhead to iterate serialized array and map it to the nested object.
Please consider this approach, which is less complicated:
let userData = new FormData($(e.currentTarget)[0]);
let userDataObject = serializedToObject(userData);
function serializedToObject(data) {
return {
provider: data.get('provider'),
pop3: {
host: data.get('pop3-host'),
port: data.get('pop3-port'),
ssl: data.get('pop3-ssl')
},
imap: {
host: data.get('imap-host'),
port: data.get('imap-port')
},
email: data.get('email'),
password: data.get('password')
};
}
I guessed some fields here. Please correct them if needed.

Related

Compare and get difference from two object for patch api request in react js?

i m having two objects previous and new one, i trying to compare and get difference for those objects, send to as patch payload from patch api,
compare each properties in object if any of the property has any difference i want all those difference in new object as payload
How can i achieve this please help me find the solution?
Is there any lodash method for this solution?
let obj = {
Name: "Ajmal",
age: 25,
email: "ajmaln#gmail.com",
contact: [12345678, 987654321],
address: {
houseName: "ABC",
street: "XYZ",
pin: 67891
}
}
let obj2 = {
Name: "Ajmal",
age: 25,
email: "something#gmail.com",
contact: [12345678, 11111111],
address: {
houseName: "ABC",
street: "XYZ",
pin: 111
}
}
result payload i m expecting would look like
let payload = {
email: "something#gmail.com",
contact: [12345678, 11111111],
address: {
pin: 111
}
}
Mista NewbeedRecursion to your service:
const compare = (obj1, obj2) => {
const keys = Object.keys(obj1);
const payload = {};
keys.forEach((el) => {
const first = obj1[el];
const second = obj2[el];
let check;
if (first !== second) {
if (first instanceof Object && !(first instanceof Array))
check = compare(first, second);
payload[el] = check || second;
}
});
return payload;
};
Here is a approach with immer that may guide you to a proper solution
This is not a generic approach but makes things easier by relying on immer
import produce, { applyPatches, enablePatches } from "immer";
import { difference } from "lodash";
// once in your app
enablePatches();
let obj = {
Name: "Ajmal",
age: 25,
email: "ajmaln#gmail.com",
contact: [12345678, 987654321],
address: {
houseName: "ABC",
street: "XYZ",
pin: 67891
}
};
let obj2 = {
Name: "Ajmal",
age: 25,
email: "something#gmail.com",
contact: [12345678, 11111111],
address: {
houseName: "ABC",
street: "XYZ",
pin: 111
}
};
Gettig the patch updates
let fork = { ...obj };
let changes = [];
const updatedItem = produce(
fork,
(draft) => {
// object specific updates
draft.Name = obj2.Name;
draft.age = obj2.age;
draft.email = obj2.email;
draft.address.houseName = obj2.address.houseName;
draft.address.street = obj2.address.street;
draft.address.pin = obj2.address.pin;
const originalContact = original(draft.contact);
const contactDiff = difference(obj2.contact, originalContact);
console.log("diff", contactDiff);
if (contactDiff?.length) {
draft.contact = contactDiff;
}
},
(patches) => {
changes.push(...patches);
}
);
//Problem here => default values need to be given to state
// so the default values need to be excluded from the patch
let state = { contact: [], address: {} };
const patch = applyPatches(state, changes);
console.log("patch", patch);
logs changes op
contact: Array(1)
0: 11111111
address: Object
pin: 111
email: "something#gmail.com"
Hope this helps you in some way
Cheers

Don't store keys with empty string values in mongodb document

i would like to store a post as a document in mongodb. I’m using mongoose for modelling and the content is created by a user using a form. The content of the form is append to FormData and sending to server. This works so far. The only issue is, that empty fields, that are appended as empty strings in the req.body will be stored in the document. The minimalize-property of my dataschema is already set true …
const post = req.body;
await Post.create(post);
req.body looks like:
[Object: null prototype] {
image: '',
title: 'hi',
subtitle: '',
category: 'Jobs',
tags: '',
text: '',
contactperson: '',
contact: '',
author: 'Felicia',
expires: '2022-08-06'
}
My document looks exactly the same, but i would like to make it look like this:
{
title: 'hi',
category: 'Jobs',
author: 'Felicia',
expires: '2022-08-06'
}
Thanks so much for your help!
You could build an object by filtering the req.body empty properties with:
const post = {};
for (const key in req.body) {
const value = req.body[key];
if (value && value !== '') {
post[key] = value
}
}
await Post.create(post);
let post = {
image: '',
title: 'hi',
subtitle: '',
category: 'Jobs',
tags: undefined,
text: null,
contactperson: '',
contact: '',
author: 'Felicia',
expires: '2022-08-06'
};
let payload ={}
Object.keys(post).filter((key) => !!post[key] && (payload[key] = post[key]));
console.log(payload)
You could use the set method for Mongoose Schemas:
const mySchema = new mongoose.Schema(
{
myAttribute: {
type: String,
set: (attribute: string) => attribute === '' ? undefined : attribute,
},
},
{ strict: 'throw' },
);
This will unset the field if the string equals ''.
Use this to trim the strings:
set: (a: string) => a?.trim() === '' ? undefined : a

Node.js undefined properties

I'm trying to create a log in window. My code is simple.
var employees = {
'1' : {
firstName: '',
lastName: '',
login: 'qwerty',
password: '12345',
},
'2' : {
login: 'asdfg',
password: '12345',
},
};
app.post('/main', function(req, res) {
if (!req.body) return res.sendStatus(400);
console.log(req.body);
for (var key in employees) {
console.log(key['login']);
console.log(key['password']);
if ((key.login == req.body.login) && (key.password == req.body.password)) {
res.render('main');
} else {
app.get('/', function(req,res) {
res.send(createIndexPage());
});
};
};
});
Why key.login and key.password return undefined?
And why else block does not run when if statement is wrong?
Look at what the value of key actually is:
var employees = {
'1': {
firstName: '',
lastName: '',
login: 'qwerty',
password: '12345',
},
'2': {
login: 'asdfg',
password: '12345',
},
};
for (var key in employees) {
console.log(key);
}
It is the property name (as a string), not the value of the property.
console.log(employees[key]['login']); will give you what you are looking for.

How do you iterate over Mongoose model properties within a middleware function?

I'm attempting to iterate over fields in a mongoose model within a middleware function. The current context this is the model object itself. So I have a function in which the context is a Javascript object like this:
{
lastName: 'Wilkens',
firstName: 'Elepart',
name: 'Elepart Wilkens',
username: 'eK',
bio: '<script>alert(\'this is a hack!!\')',
_id: 53b17dd0e8c5af50c1d73bc6,
language: 'en',
country: 'USA',
online: true
}
I want to iterate over this object (which is represented in the current function with this). Every time I attempt to iterate with loops, it prints out values that look like internal Javascript metadata. Is it possible to iterate over this within a function if this represents an object?
Here's the actual middleware function:
userSchema.pre('save', function (next) {
console.log(this); // This prints precisely the same object I have copied above
var fields = Object.keys(this);
for(var i = 0; i < fields.length; i++) {
console.log(this[fields[i]]);
}
for(var key in this) {
if(this.hasOwnProperty(key)) {
console.log(this[key]);
}
}
});
And the output is:
{
strictMode: true,
selected: undefined,
shardval: undefined,
saveError: undefined,
validationError: undefined,
adhocPaths: undefined,
removing: undefined,
inserting: undefined,
version: undefined,
getters: {},
_id: undefined,
populate: undefined,
populated: undefined,
wasPopulated: false,
scope: undefined,
activePaths:
{ paths:
{ username: 'modify',
firstName: 'modify',
name: 'modify',
online: 'default',
country: 'default',
language: 'default',
_id: 'default',
bio: 'modify',
lastName: 'modify' },
states: { default: [Object], init: {}, modify: [Object], require: {} },
stateNames: [ 'require', 'modify', 'init', 'default' ],
map: [Function] },
ownerDocument: undefined,
fullPath: undefined }
true
undefined
0
{ online: true,
country: 'USA',
language: 'en',
_id: 53b1825a00ed9af7c12eedf9,
bio: '<script>alert(\'this is a hack!!\')',
username: 'yK',
name: 'Yershay Wilkens',
firstName: 'Yershay',
lastName: 'Wilkens' }
{ save:
[ { [Function] isAsync: false },
{ [Function: checkForExistingErrors] isAsync: false },
{ [Function: validation] isAsync: false },
{ [Function] isAsync: false },
{ [Function] isAsync: false } ],
validate: [ { [Function] isAsync: false } ] }
{ save: [], validate: [] }
{ [Function] numAsyncPres: 0 }
{ [Function] numAsyncPres: 0 }
{}
You're using integer indexes instead of the string references from the fields array. It should be:
var fields = Object.keys(this);
for(var i = 0; i < fields.length; i++) {
console.log(this[fields[i]]);
}
(e.g., you were doing this[1], this[2], instead of this[fields[1]])
#JohnnyHK's comment worked for me:
const user = new User();
const schemaKeys = Object.keys(user.toObject());
I found a slightly different way to accomplish what I wanted which was to iterate over the model properties of a Mongoose model within a middleware function. This uses async.js, but you could refactor it to use a generic JS loop or any other control flow library. The key is to get an array of the document's fields, then you can iterate over those and get/set the values using the current context with this. As far as I know, this will not coerce non-string values into strings. I've tested it with strings, numbers, booleans and objectIds and they are successfully saved as their original data types.
yourSchema.pre('save', function (next) {
var self = this;
// Get the document's fields
var fields = Object.keys(this._doc);
// Do whatever you want for each field
async.each(fields, function(field, cb) {
self[field] = validator.escape(self[field]);
cb();
}, function(err){
next();
});
});

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