I have this code that formats an array of objects, which is set out how I want it. However, when I go to return the output something strange happens. If I were to just return alert_cache, it returns null. But If I were return it like alert_cache.workflow_steps it returns the data needed.
Does anyone have any idea how to get around this?
if (alert_cache.length == 0) {
alert_cache.workflow_steps = {}
alert_cache.workflow_steps[keys.workflow_step] = { "errors": [], "last_error": {}};
let alr = alert_cache.workflow_steps[keys.workflow_step];
alr.errors.push(now)
alr.last_error = {message: keys.message, url:alert.step_log_url}
}
return alert_cache;
You're using alert_cache like an array and like an object. You're checking length (as if it were an array):
if (alert_cache.length == 0) {
but you're also assigning to a non-element property:
alert_cache.workflow_steps = {}
Note that doing that will not change length.
You haven't shown how you create alert_cache to start with, but if it's an array, and if you're then using it with something that only looks at its array entries and not at its other properties (for instance, JSON.stringify), it will be empty (not null).
Related
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);
I have an object array that looks like this:
UserForm
{
name:"Tom",
occupation:"Programmer",
hobbies:" ",
foodAllergy:"fish",
favoriteColor:"blue"
}
And an ValidateFieldsArray that looks like this:
["hobbies", "foodAllergy", "name"]
I need to validate that there are strings filled in from the ValidateFieldsArray in the UserForm object array. It would return false because hobbies is empty.
Currently I'm using a For loop to traverse through the validateFieldsArray and it works fine. I'm wondering if there is a better solution. I also trim the string.
I can't use lodash because I'm comparing the key not the value. I want to do something like this and add additional checks like string.trim() !=="":
_.result(_.find(UserForm, { key in ValidateFieldsArray}), value);
Using Array.every seems more appropriate to check every key in an array
var isValid = ValidateFieldsArray.every( v => UserForm[v] && UserForm[v].trim().length);
let isValid = validateFieldsArray.reduce((obj, k) => {
return (obj && obj[k].trim()) ? obj : false;
}, UserForm);
Returns the UserForm object if valid otherwise returns boolean false. Object must have a string that has more than just whitespace. If you replace let with var and the arrow function then the code is valid ES 5 and works back to IE 9.
If we have an array that does not exists and we check the value of the array it gives me an error. "variable is not defined"
for example I have:
var arr = new Array();
arr['house']['rooms'] = 2;
and I use
if ( typeof arr['plane']['room'] != 'undefined' ) )
it says arr['plane'] not defined...
I don't want to use this:
if ( typeof arr['plane'] != 'undefined' ) ) {
if ( typeof arr['plane']['room'] != 'undefined' ) {
}
}
In php I use isset that works nice for me, I searched a lot on google to find the answer but I can't...
The thing to realize is that there are no multi-dimensional arrays in javascript. It is easy to make an array element contain an array, and work with that, but then all references you make have to use that consideration.
So, you can do
arr = []; // or more appropriately {}, but I'll get to that soon
arr['house'] = [];
arr['house']['rooms'] = 2;
But doing
arr['house']['rooms'] = 2;
should give you an error unless you've already defined arr and arr['house'].
If you've defined arr but not arr['house'], it's valid syntax to reference arr['house'] - but the return value will (appropriately) be undefined.
And this is where you're at when you're looking at arr['plane']['room']. arr is defined, so that's ok, but arr['plane'] returns undefined, and referencing undefined.['room'] throws an error.
If you want to avoid the errors and have multiple levels of reference, you're going to have to make sure that all the levels but the lowest exist.
You're stuck with if (arr && arr['plane'] && arr['plane']['room']).
Or perhaps if (arr && arr['plane'] && room in arr['plane'] would be more accurate, depending on your needs. The first will check if arr['plane']['room'] has a truthy value, while the second will check if arr['plane']['room'] exists at all (and could have a falsey value).
Arrays vs objects
Arrays and objects are very similar and can both be accessed with [] notation, so it's slightly confusing, but technically, you're using the object aspect of the array for what you're doing. Remember, all arrays (and everything other than primitives - numbers, strings and booleans) are objects, so arrays can do everything objects can do, plus more. But arrays only work with numeric indices, i.e. arr[1][2]. When you reference an array with a string, you're attempting to access the member of the underlying object that matches that string.
But still in this case, it doesn't matter. There are no multi-dimensional arrays - or objects.
The [] notation with objects is simply a way to check for members of objects using a variable. arr['plane']['rooms'] is actually equivalent to arr.plane.rooms. but perhaps the arr.plane.room notation will help make it more clear why you have to first check arr.plane (and arr).
Use the following if you want to test for existence in an object:
if ( 'plane' in arr && 'room' in arr.plane ) {
// Do something
}
That's not an array, but an object, aka associative array. You can declare it like this:
var aarr = { house: { rooms: 2 } };
Now you can do:
if (aarr.house && aarr.house.rooms) {/* do stuff */ }
or uglier, but shorter:
if ((aarr.house || {}).rooms) {/* do stuff */ }
See also...
To more generally traverse an object to find a path in it you could use:
Object.tryPath = function(obj,path) {
path = path.split(/[.,]/);
while (path.length && obj) {
obj = obj[path.shift()];
}
return obj || null;
};
Object.tryPath(aarr,'house.rooms'); //=> 2
Object.tryPath(aarr,'house.cellar.cupboard.shelf3'); //=> null
JsFiddle
I run a timed loop which fetches data asynchronously from the server and updates an observable array. I thought that this would prevent dups but it doesn't seem to. How can I prevent adding duplicates?
// Operations
self.addDevice = function (device) {
if (device != null && ko.utils.arrayIndexOf(self.devices, device) < 0) {
self.devices.push(device);
}
}
This is always returning true, as in the array does not contain the particular device (though it clearly does).
The updates you get may have all the same values as objects you have in your array, but they're probably different objects, so a simple equality check will return false. You'll have to supply a callback to test for equality yourself by comparing properties within the objects.
For example, if a = {prop: 5} and b = {prop: 5}, then a == b returns false. You need to pass in a function to ko.utils.arrayFirst or ko.utils.arrayFilter like
var newItem = new Item();
ko.utils.arrayFirst(self.items(), function(existingItem, newItem) {
return existingItem.prop == newItem.prop;
}
I'm trying to create a multi-dimensional array.
My assumption that the following structure stuff['mykey1']['mykey2']['mykey3'] can be interpreted as stuff is an array of two-dimensional arrays. And stuff['mykey1'] will return me a two dimensional array with following keys ['mykey2']['mykey3']
I try to create this structure like so:
var stuff = null;
if(stuff === null)
{
stuff = []; // stuff is []
}
if(stuff[userId] === undefined)
{
stuff[userId] = []; // stuff is [undefined, undefined, undefined, 888087 more...]
}
if(stuff[userId][objectId] === undefined)
{
stuff[userId][objectId] = [];
}
However, when I look at stuff array as I step through, I see that after stuff[userId] = []; stuff array is [undefined, undefined, undefined, 888087 more...]
I'm expecting [888087, []]
Where do the undefined values come from?
Where do the undefined values come from?
You are using Arrays, not objects. If you add a numerical property on an Array object, it's length will be updated and the other indices stay unitialized (sparse array), but are displayed as undefined (see What is "undefined x 1" in JavaScript?).
Instead, use normal objects, where numerical properties have no special behavior:
var stuff = null;
if(stuff === null)
{
stuff = {}; // stuff is an empty object
}
if(stuff[userId] === undefined)
{
stuff[userId] = {}; // stuff is now enriched with one property
}
if(stuff[userId][objectId] === undefined)
{
stuff[userId][objectId] = {}; // or maybe you really want an array here?
}
Its because of usage of arrays. The length of the remainin elements is made undefined.
For example if a(1) is specified, a(0) will be undefined
You are trying to create associative arrays, and in JavaScript this is done with... objects, not arrays!
So at each step you need to use {} instead of [] to create the next level. And you need to use a for...in loop to iterate through the keys.
For more details, search the Web for JavaScript associative arrays". For example:
https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Working_with_Objects
The question is long answered, but I want to chip in with this shorthand, that really makes it more compact and readable:
stuff = stuff || {};
// if stuff is already defined, just leave it be. If not (||), define it as object
stuff[userId] = stuff[userId] || {};
// if stuff[userId] is already defined, define it as self (let it be unchanged). If not defined ( the || -signs ), define it as object.
stuff[userId][objectId] = stuff[userId][objectId] || {};
// and so on :)