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 :)
Related
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).
Essentially my I am trying to initialize a JavaScript object and have it contain empty objects with a single key. For example:
getOject('one.two.three')
Would result in the object:
{one:{two:{three:''}}}
As far as I can tell, you can't initialize with dynamic key names unless you use array notation
root[dynamicKey] = 'some variable';
so I need to loop through and based on the number of args initialize each one then assign it's value but the syntax doesn't seem to let me do this in any way that I know of.
So, if it were not a loop it would be like this:
jsonifiedForm[rootKey] = {};
jsonifiedForm[rootKey][childKeys[0]] = {};
jsonifiedForm[rootKey][childKeys[0]][childKeys[1]] = $input.val();
I can't think of a way to do this, I am not typically a JS guy so it might be something simple but I couldn't find anything on Google or Stack Overflow
Thank you in advance!
This function should be what you're looking for.
function getOject(str) {
// this turns the string into an array = 'one.two.three' becomes ['one', 'two', 'three']
var arr = str.split('.');
// this will be our final object
var obj = {};
// this is the current level of the object - in the first iteration we will add the "one" object here
var curobj = obj;
var i = 0;
// we loop until the next-to-last element because we want the last element ("three") to contain an empty string instead of an empty object
while (i < (arr.length-1)) {
// add a new level to the object and set the curobj to the new level
curobj[arr[i]] = {};
curobj = curobj[arr[i++]];
}
// finally, we append the empty string to the final object
curobj[arr[i]] = '';
return obj;
}
Because JavaScript references values in variables instead of copying them "into" variables, we can make our initial value, then make a reference to it which we'll move around as we delve down in:
var getOject = function (k, s) {
// initialize our value for return
var o = {},
// get a reference to that object
r = o,
i;
// we'll allow for a string or an array to be passed as keys,
//and an optional sepeartor which we'll default to `.` if not given
if (typeof k === 'string') {
k = k.split(s || '.');
}
// do we have an array now?
if (k && k.length) {
//iterate it
for (i = 0; i < k.length; i += 1) {
// set a property on the referenced object
r[k[i]] = {};
// point the reference to the new level
r = r[k[i]];
}
}
// send back the object
return o;
}
console.log(getOject('one.two.three'));
console.log(getOject('four|five|six', '|'));
r points to the same thing that o does, initially, and as we move the reference (r) deeper into o and write to it, we're building out o as we go.
The two console.log() calls at the end output the following:
Also notice I let you pass in an array to start with if you feel like it, and made the separator a parameter so that you're not stuck with .
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 wanted to write one version of a function that iterated over both Array objects and Objects, with minimal duplicate code. Something like:
function (arr_or_obj) {
arr_or_obj.forEach( function(key,value) {
// do stuff with key and value
});
}
This was before I realized that (in Chrome at least), Object.keys returns a list of the keys for an array. So I could use that to treat the Array like an object. Like so:
function (arr_or_obj) {
var keys = Object.keys(arr_or_obj);
keys.forEach( function(key) {
var value = arr_or_obj[key];
// do stuff with key and value
});
}
Problem solved. But this was not before I wrote my own "JavaScript pseudo-Array iterator". The only advantage of this was that instead of getting a list of keys for the Array (which could be very long), we save memory by producing only the return values we need. Like so:
function create_iterator(max) {
var jkeys = {};
jkeys.count_ = 0;
jkeys.length = max;
jkeys.getter_ = function() {
var returnable = jkeys.count_;
jkeys.count_ += 1;
jkeys.__defineGetter__(jkeys.count_,jkeys.getter_);
delete jkeys[jkeys.count_-1];
return returnable;
};
jkeys.__defineGetter__(0, jkeys.getter_);
return jkeys;
}
Which you can then call by going:
var z = create_iterator(100);
z[0];
>> 0
z[0];
>> undefined
z[1];
>> 1;
z[2]
>> 2;
...
This is sort of a question and answer in one, but the obvious question is, is there a better way to do this without using Object.keys?
Object.keys gives you an array of the object's own enumerable properties. That is, properties it has itself, not from its prototype, and that are enumerable (so not including things like length on arrays, which is a non-enumerable property).
You can do the same thing with for-in if you use correct safeguards:
var key;
for (key in arr_or_obj) {
if (arr_or_obj.hasOwnProperty(key)) {
// do something with it
}
}
Now you're doing the same thing you were doing with Object.keys.
But, note that this (and Object.keys) will include properties people put on arrays that aren't array indexes (something which is perfectly valid in JavaScript). E.g.:
var key;
var a = [1, 2, 3];
a.foo = "bar";
for (key in a) {
if (a.hasOwnProperty(key)) {
console.log(key); // Will show "foo" as well as "0", "1", and "2"
}
}
If you want to only process array indexes and not other properties, you'll need to know whether the thing is an array, and then if so use the "is this an index" test:
var key;
var isArray = Array.isArray(arr_or_obj);
// Or (see below):
//var isArray = Object.prototype.toString.call(arr_or_obj) === "[object Array]";
for (key in arr_or_obj) {
if (a.hasOwnProperty(key) &&
(!isArray || isArrayIndex(key))
) {
console.log(key); // Will only show "0", "1", and "2" for `a` above
}
}
...where Array.isArray is a new feature of ES5 which is easily shimmed for older browsers if necessary:
if (!Array.isArray) {
Array.isArray = (function() {
var toString = Object.prototype.toString;
function isArray(arg) {
return toString.call(arg) === "[object Array]";
}
return isArray;
})();
}
...and isArrayIndex is:
function isArrayIndex(key) {
return /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294; // 2^32 - 2
}
See this other answer for details on that (where the magic numbers come from, etc.).
That loop above will loop through an object's own enumerable properties (all of them) if it's not an array, and loop through the indexes (but not other properties) if it's an array.
Taking it a step further, what if you want to include the properties the object gets from its prototype? Object.keys omits those (by design). If you want to include them, just don't use hasOwnProperty in the for-in loop.
You can try something like this...
Live Demo
function forIn(obj) {
var isArray = Array.isArray(obj),
temp = isArray ? obj : Object.keys(obj);
temp.forEach(function (value, index, array) {
console.log(this[isArray ? index : value]);
}, obj);
}
You can write:
for (key in arr_or_obj) {
if (arr_or_obj.hasOwnProperty(key) {
value = arr_or_obj[key];
// do stuff with key and value
}
}
Let's say I have an object:
var foo = {'bar1': {'baz1':1}};
And I try to access foo['bar2']['baz2']. If I was just trying to access foo['bar2'], JS would return undefined. But because I'm trying to get the next property after the undefined bar2, JS throws TypeError: Cannot read property 'baz2' of undefined.
Is there some automatic accessor for an object that first checks if baz2 exists in foo before trying to call its properties? I know I could use a try/catch block or an if statement, but I was hoping for a function along the lines of C++'s array::at, that would check for me.
You could write your own pretty easily:
function at(obj, property) {
var props = property.split('.');
for(var i = 0; i < props.length; i++) {
if (typeof obj === 'undefined') {
return;
}
obj = obj[props[i]];
}
return obj;
}
var foo = {'bar1': {'baz1':1}};
console.log(at(foo, 'bar1.baz1'));
// => 1
console.log(at(foo, 'bar2.baz2'));
// => undefined
Keep in mind this is ONLY for objects, like the one you have shown, and NOT for arrays (ie [0, 1, 2]) but my favorite is this one:
if ('bar1' in foo)
This is even useful for, say, HTML5 feature detection.
if ('localStorage' in window)
I could give you one for arrays too, but I feel like the more logical thing would be to compare its length to a given index. And...not insert undefined values into arrays. Y'know. =p
You can use the in operator:
if("bar2" in foo) {
//do stuff with foo['bar2']
}
or you can check to see if foo['bar2'] is undefined:
if(typeof foo['bar2'] !== "undefined") {
//do stuff with foo['bar2']
}
Also, what you're working with are objects and not arrays (well, they're associative arrays, but also objects in JavaScript).
foo['bar2'] && foo['bar2']['baz2'] will do the trick as well.