How to iterate over different variables/states to create an object? - javascript

I need help iterating over variables/state elements to create an object. I am going to iterate over firstName state, lastName state, and many more states. The code is getting way too complicated. Was wondering if there is a proper way to build an object for a graphql call? or just a better way to build an object?
I have a profile page that has forms for updating the user's account. Each form input has state and setState props. On submit of the form these values need to be checked against the previous queried information stored in the account object stored in state. I am using a graphql endpoint and when I call make the update account call, I have 3 choices for each parameter value in the graphql call For example, firstName can be
1) Null (delete in database)
2) Some non-null value (change to this value in database)
3) field does not exist in call graphql call (do nothing to that attribute)
The code below I have to do for 20 state values which is not practical.
// If there is a difference between client's firstName and queried firstName in server
if (firstName != account.firstName) {
//If we stored firstName as empty String
if(firstName === ''){
//If the firstName queried is not empty
if(account.firstName != null) {
//Set the objects first name attribute to null, so server deletes field
input['firstName'] = null;
} else {
//firstName is empty string and server value is null, so do nothing, do not add attribute firstName to object "input"
}
} else {
//firstName is not an empty string so add it to object value
input['firstName'] = firstName;
}
}
If there is a better way to do this I would love the help.

One way around it is to use the bracket notation to extract specify the key for which you want to extract the value and the value that it needs to be checked against.
function getComputedValue(value, key) {
if (value != account[key]) {
if (value === '') {
if (account[key] != null) {
input[key] = null;
} else {
}
} else {
input[key] = value;
}
}
}
let firstName = getComputedValue("Tom", "firstName");
let lastName = getComputedValue("Smith", "lastName");

There are many ways you can approach this problem. I suggest you use some of the functions Underscore JS provides :
pairs_.pairs(object)
Convert an object into a list of [key, value] pairs. The opposite of object.
_.pairs({one: 1, two: 2, three: 3});
=> [["one", 1], ["two", 2], ["three", 3]]
Then you can iterate through the array
_.each(array, function(pair) {
// Insert the input assignment code here for each key
});

Related

How do I only add the field in an object if the value isn't null? Javascript

I am currently using mongoose to write data to a document in a MongoDB collection but I accept no null fields, I already have set default values in the document.
I call an update function that has some fields as null, those fields that are null already, I do not want them to make a modification.
Example:
const Business = require("./businessModel") //This references the model
const {id, email, name, contactNumber} = args
const business = await Business.findByIdAndUpdate(
{ id},
{
name: ((name != null) ? name : (skip this field))... //HERE
});
Where I commented here, if the name is not null, which means a value exists for it then have the predefined schema value name now set to the new name input, otherwise don't change anything and just skip the field.
I do already have an alternative where I call the document first then replace it with the default value for the document but that requires a document call which I do not believe to be an optimal solution.
It looks like your args variable is an object with the relevant fields.
Instead of destructuring all the individual properties, you can extract just the id and leave the ...rest. You can then filter this rest object for null properties.
// mock
const args = { id: 1, name: null, email: 'email#domain', contactNumber: 4 };
//const Business = require("./businessModel") //This references the model
const { id, ...rest } = args;
const update = Object.fromEntries(Object.entries(rest).filter(([, v]) => v != null));
console.log(update);
//const business = await Business.findByIdAndUpdate({id}, update);
More options for object property filtering here: Remove blank attributes from an Object in Javascript
Note: NULL doesn't exist, the null object in javascript is lowercase null. see: why using NULL with logical operator throws error in JS
Try using update-validators. So in your case, check if value in name field === null, and handle the error response as required.

Programmatically pushing to an array of type any when we don't know the property names in Typescript

I created an Angular dropdown list component with an items property such as:
items: any [];
The user of this component is able to set the items to an array of anything such as:
[{id: "1", xyz: "hello"}, {id: "2", xyz: "world}]
or
[{abc: "1", def: "john"}, {abc: "2", def: "doe"}]
The user can additionally set the itemText and itemValue as two separate properties:
In the component they are declared as:
itemText: string;
itemValue: string;
I am trying to create another set of properties called customItemtext and customItemValue. This will give the user the ability to add a custom option to the beginning of the items array that wasn't originally part of the datasource. The problem here is that I would need to push the item without knowing the property names since they technically could be anything.
I was originally thinking to just get the property names from the itemText and itemValue properties but this isn't possible since the user can use formatting when setting the values of them.
Note: When the user sets the items array it comes directly from a query so the user has no flexibility in adding the custom items themselves. It needs to be done by my component through properties.
Wow I just built this: InputTagComponent
When you send the typeahead source it must be an array of objects. Then, similar to yours, I use filterKeys = ['firstName','lastName'] and displayKeys = ['firstName'].
I don't allow users to send along a string when objects are being used. But you can!
Here is how I'm adding user selected data to the form array:
addTag(value: any):void {
// disallow blanks, duplicates, enforce maxTags limit and disallow string entries when displayKeys is set
if (
value == '' ||
this.maxTags === this.tagsArray.length ||
this.duplicates(value) ||
(this.displayKeys !== null && typeof value === 'string')
) { return; }
// if value is a string of comma-separated entries
if (typeof value === 'string' && value.includes(',')) {
for (let i = 0, j = value.split(",").length; i < j; i++) {
this.tagsArray.push(new FormControl(value.split(",")[i]));
this.onChange(this.tagsArray);
}
this.inputValue = '';
this.float = true;
return;
}
this.inputValue = '';
this.float = true;
this.tagsArray.push(new FormControl(value));
this.onChange(this.tagsArray); // update controller value
}
Removal is easy
removeTag(i: number):void {
this.tagsArray.removeAt(i);
this.onChange(this.tagsArray); // update controller value
}
Duplicate checks for objects are stringified, which also compares string input...
duplicates(value: any): boolean{
const test = JSON.stringify(value);
let match: boolean;
// test for duplicate objects in tag array
for ( let i = 0, j = this.tagsArray.value.length; i < j; i++ ) {
let controlVal = JSON.stringify(this.tagsArray.value[i]);
if (test === controlVal) {
return true;
} else {
match = false;
}
}
return match;
}
I hope this helps but please let me know if I can help more.
Also FWIW I'm having an issue with this here is you have any insight: When is Angular's FormArray a traditional array and when is it a FormArray object?

Check if object already exists in object

I want to check if an object already exists in a given object by only having the object.
For instance:
const information = {
...
city: {
Streetname: ''
}
}
Now, I get the city object and want to check if it is already in the information object (without knowing the property name). The city could be n deep in the information object.
To get the property name of an object you can use Object.keys(). The first problem solved.
Now we need to iterate through the whole object including nested objects. This is the second problem.
And compare it to a query object. This is the third problem.
I assume that we have an object that only contains "simple" though nested objects with primitive values (I do not consider objects with functions or arrays)
// let's assume we have this object
const information = {
city: {
Streetname: 'streetname1'
},
house: {
color: "blue",
height: 100,
city: {
findMe: { Streetname: '' } // we want to get the path to this property 'findMe'
}
},
findMeToo: {
Streetname: '' // we also want to get the path to this proeprty 'findMeToo'
},
willNotFindMe: {
streetname: '' // case sensetive
}
}
// this is our object we want to use to find the property name with
const queryObject = {
Streetname : ''
}
If you use === to compare Objects you will always compare by reference. In our case, we are interested to compare the values. There is a rather extensive checking involved if you want to do it for more complex objects (read this SO comment for details), we will use a simplistic version:
// Note that this only evaluates to true if EVERYTHING is equal.
// This includes the order of the properties, since we are eventually comparing strings here.
JSON.stringify(obj1) === JSON.stringify(obj2)
Before we start to implement our property pathfinder I will introduce a simple function to check if a given value is an Object or a primitive value.
function isObject(obj) {
return obj === Object(obj); // if you pass a string it will create an object and compare it to a string and thus result to false
}
We use this function to know when to stop diving deeper since we reached a primitive value which does not contain any further objects. We loop through the whole object and dive deeper every time we find a nested object.
function findPropertyPath(obj, currentPropertyPath) {
const keys = isObject(obj) ? Object.keys(obj) : []; // if it is not an Object we want to assign an empty array or Object.keys() will implicitly cast a String to an array object
const previousPath = currentPropertyPath; // set to the parent node
keys.forEach(key => {
const currentObj = obj[key];
currentPropertyPath = `${previousPath}.${key}`;
if (JSON.stringify(currentObj) === JSON.stringify(queryObject)) console.log(currentPropertyPath); // this is what we are looking for
findPropertyPath(currentObj, currentPropertyPath); // since we are using recursion this is not suited for deeply nested objects
})
}
findPropertyPath(information, "information"); // call the function with the root key
This will find all "property paths" that contain an object that is equal to your query object (compared by value) using recursion.
information.house.city.findMe
information.findMeToo
const contains = (item, data) => item === data || Object.getOwnPropertyNames(data).some(prop => contains(item, data[prop]));
const information = {
city: {
Streetname: ''
}
}
console.log(contains(information.city, information));
console.log(contains({}, information));

How to check if an object's keys and deep keys are equal, similar to lodash's isEqual?

So I'm in a unique situation where I have two objects, and I need to compare the keys on said objects to make sure they match the default object. Here's an example of what I'm trying to do:
const _ = require('lodash');
class DefaultObject {
constructor(id) {
this.id = id;
this.myobj1 = {
setting1: true,
setting2: false,
setting3: 'mydynamicstring'
};
this.myobj2 = {
perm1: 'ALL',
perm2: 'LIMITED',
perm3: 'LIMITED',
perm4: 'ADMIN'
};
}
}
async verifyDataIntegrity(id, data) {
const defaultData = _.merge(new DefaultObject(id));
if (defaultData.hasOwnProperty('myoldsetting')) delete defaultData.myoldsetting;
if (!_.isEqual(data, defaultData)) {
await myMongoDBCollection.replaceOne({ id }, defaultData);
return defaultData;
} else {
return data;
}
}
async requestData(id) {
const data = await myMongoDBCollection.findOne({ id });
if (!data) data = await this.makeNewData(id);
else data = await this.verifyDataIntegrity(id, data);
return data;
}
Let me explain. First, I have a default object which is created every time a user first uses the service. Then, that object is modified to their customized settings. For example, they could change 'setting1' to be false while changing 'perm2' to be 'ALL'.
Now, an older version of my default object used to have a property called 'myoldsetting'. I don't want newer products to have this setting, so every time a user requests their data I check if their object has the setting 'myoldsetting', and if it does, delete it. Then, to prevent needless updates (because this is called every time a user wants their data), I check if it is equal with the new default object.
But this doesn't work, because if the user has changed a setting, it will always return false and force a database update, even though none of the keys have changed. To fix this, I need a method of comparing the keys on an object, rather any the keys and data.
That way, if I add a new option to DefaultObject, say, 'perm5' set to 'ADMIN', then it will update the user's object. But, if their object has the same keys (it's up to date), then continue along your day.
I need this comparison to be deep, just in case I add a new property in, for example, myobj1. If I only compare the main level keys (id, myobj1, myobj2), it won't know if I added a new key into myobj1 or myobj2.
I apologize if this doesn't make sense, it's a very specific situation. Thanks in advance if you're able to help.
~~~~EDIT~~~~
Alright, so I've actually come up with a function that does exactly what I need. The issue is, I'd like to minify it so that it's not so big. Also, I can't seem to find a way to check if an item is a object even when it's null. This answer wasn't very helpful.
Here's my working function.
function getKeysDeep(arr, obj) {
Object.keys(obj).forEach(key => {
if (typeof obj[key] === 'object') {
arr = getKeysDeep(arr, obj[key]);
}
});
arr = arr.concat(Object.keys(obj));
return arr;
}
Usage
getKeysDeep([], myobj);
Is it possible to use it without having to put an empty array in too?
So, if I understand you correctly you would like to compare the keys of two objects, correct?
If that is the case you could try something like this:
function hasSameKeys(a, b) {
const aKeys = Object.keys(a);
const bKeys = Object.keys(b);
return aKeys.length === bKeys.length && !(aKeys.some(key => bKeys.indexOf(key) < 0));
}
Object.keys(x) will give you all the keys of the objects own properties.
indexOf will return a -1 if the value is not in the array that indexOf is being called on.
some will return as soon as the any element of the array (aKeys) evaluates to true in the callback. In this case: If any of the keys is not included in the other array (indexOf(key) < 0)
Alright, so I've actually come up with a function that does exactly what I need. The issue is, I'd like to minify it so that it's not so big. Also, I can't seem to find a way to check if an item is a object even when it's null.
In the end, this works for me. If anyone can improve it that'd be awesome.
function getKeysDeep(obj, arr = []) {
Object.keys(obj).forEach(key => {
if (typeof obj[key] === 'object' && !Array.isArray(obj[key]) && obj[key] !== null) {
arr = this.getKeysDeep(obj[key], arr);
}
});
return arr.concat(Object.keys(obj));
}
getKeysDeep(myobj);

AngularJs how to check if a value exist in an array

I am trying to loop through an array to check if email, phone and alternate phone exist in database my issue is I can't find any function or work around in Angularjs that can help me loop through the array where I have set the the listed through variable
$scope.dataCheck = {
email: $scope.TheEmail,
phone: $scope.ThePhone,
AltPhone: $scope.TheAltPhone
}
I have tried to use indexOf like below still not working
if ($scope.dataCheck.indexOf($scope.TheEmail)) {
//I call a function to check if email exist and return
}
Thank You
I should mention I am using ExpressJs and I am new to Javascript
While you are technically correct that objects in JavaScript are associative arrays, they are not Arrays as such. Compare the object property to the value you are searching for.
$scope.dataCheck = {
email: $scope.TheEmail,
phone: $scope.ThePhone,
AltPhone: $scope.TheAltPhone
}
if ($scope.dataCheck.email === $scope.TheEmail) {
//I call a function to check if email exist and return
}
If you simply want to see if the has a value, you can check
if (typeof $scope.dataCheck.email !== "undefined") { ... }
if (myArray.filter(x => !x.TheEmail || !x.ThePhone || !x.TheAltPhone).length == 0) {
// do something
}
if a value is undefined, null or empty ("") javascript will return false. Filter function will return values which contains the expression and count them with length without loop through all items.
about filter you can find here more information: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/filter?v=control
Hope this helps.

Categories