find Javascript object length - javascript

Im trying to find the length of the report_data(object) key using the below code..but for some reasons it yields value 3.
a={report_freq: "daily", report_item_num: 2, report_num: 39, report_data: "{}"}
Object {report_freq: "daily", report_item_num: 2, report_num: 39, report_data: "{}"}
Object.getOwnPropertyNames(a.report_data).length
3
for more clarity I have the image.

a.report_data is a string with three properties:
0, representing the first character ("{").
1, representing the second character ("}").
and length, representing the length of the string (2).
It's a little counter-intuitive, if you come from other languages, that 0 and 1 are properties, but in Javascript array elements are properties just like all other properties, and "regular" properties can be accessed using array syntax (aka "bracket notation"):
// "array elements"
a.report_data[0] === "{";
a.report_data[1] === "}";
// or...
a.report_data["0"] === "{";
a.report_data["1"] === "}";
// "normal" properties
a.report_data.length === 2;
// or...
a.report_data["length"] === 2;
These are all property names, and, thus, when you ask for an array of property names for your string, you get:
["0", "1", "length"]

Assuming you want the length of the actual string value, then you simply want to use report_data.length, as demonstrated here:
var a = {
report_freq: "daily",
report_item_num: 2,
report_num: 39,
report_data: "{}"
};
console.log(a.report_data.length)
Your current code includes this:
Object.getOwnPropertyNames(a.report_data).length
If you look at the docs for Object.getOwnPropertyNames(obj), you'll see the following description:
Object.getOwnPropertyNames() returns an array whose elements are strings corresponding to the enumerable and non-enumerable properties found directly upon obj.
So, in this case, Object.getOwnPropertyNames(a.report_data) returns an array containing the keys found on the string, and there happens to be 3 of them.

Related

What is the difference between using a string or a number to index an array in Javascript? [duplicate]

This question already has answers here:
Is a JavaScript array index a string or an integer?
(5 answers)
Closed 4 years ago.
In Javascript it is possible to use a number (integer) or a string (or a char) to index an array for example:
array[0] = true;
or
array['0'] = true;
Does the computer memory work different depending on how you index the array or is it the exact same thing to do it both ways?
The indexes are stored internally as strings.
But it's more common practise to use numbers to access an array by it's index.
A property name P (in the form of a String value) is an array index if
and only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not
equal to 2^³²−1
It's explained in this old post
Below is a snippet that showcases it.
Accessing the index with a string works fine as long that index string only contains digits.
But by expressing the index as a word then it can only be accessed with that word. And console logging the array doesn't show it.
let arr = [0,'a one','a two','a three'];
arr['four'] = 'a four';
arr.push('5');
arr.push(6);
arr.push([7,'7']);
arr.push({a:8, b:'8'});
console.log('\nLog the array:\n\n');
console.log(arr);
console.log('\nAccessing the array:\n\n');
console.log("arr[0]:\t" + arr[0]);
console.log("arr[\'1\']:\t" + arr['1']);
console.log("arr[\'two\']:\t" + arr['two']);
let i=2;
console.log("arr[++i]:\t" + arr[++i]);
console.log("arr[\'four\']:\t" + arr['four']);
console.log('arr[4]:\t'+ arr[4]);
console.log('arr[5]:\t'+ arr[5]);
console.log('\nListing the types in the array:\n\n');
for (var a in arr) console.log(a+'\tindex type: '+ typeof a +', value: '+ arr[a] + ', value type: '+ typeof arr[a]);
And here's a snippet to compare speed between using a number or a string in your browser. It should take roughly the same time.
let arr1 = [];
console.time('Access array through string');
for(let i = 0; i <= 42000000;i++){
arr1['42000000'] = 42;
}
console.timeEnd('Access array through string');
let arr2 = [];
console.time('Access array through integer');
for(let i = 0; i <= 42000000;i++){
arr2[42000000] = 42;
}
console.timeEnd('Access array through integer');
The answer is simple: there is no difference.
Javascript arrays are objects. All keys of objects are strings (or symbols), but never numbers.
Property names must be strings. This means that non-string objects
cannot be used as keys in the object. Any non-string object, including
a number, is typecasted into a string via the toString method. ...see more here
The property accessor [] converts to string first before looking for the property. (some engines may optimize this step and not perform a proper toString call, but it's of no concern here)
So array[0] is interpreted as array['0'].
const a = {
toString: function () {
console.log("a.toString called")
return "1";
}
};
const array = ['a','b','c'];
console.log(array[a]);
When you use quotations for indexing, you're creating a key-value pair in the array. I recommended you stick to numerical notation unless you're intentionally creating those pairs; while array['0'] technically posts to the position, it's bad practice.
Even though myArr['two'] doesn't seem to want to show up in the snippet output, if you hit F12 and look at the console output, you'll clearly see it displayed.
["String 0", "String 1", two: "String 2"]
let myArr = [];
myArr[0] = "String 0";
myArr['1'] = "String 1";
myArr['two'] = "String 2";
console.log(myArr);
As the mdn docs state, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
Arrays are actually just objects that have array-like properties. Keys in JavaScript are strings. So when you see an array, it's actually just a set of key-value pairs, where the keys are index numbers.
Arrays are a special data type. Adding keys that do not correlate to possible indexes are added to an 'object property collection' and are not returned when you use the object.
I think the chrome console does the best job of displaying this:
This array is actually an object with keys of 0 1 2 3, a length property, and a prototype.

Multiple objects inside main object cannot be sorted by key value, but objects inside an array can be - how and why does this happen?

This is a follow up to a previous question I asked:
Sort JSON response by key value
So I know that objects cannot be sorted using the .sort method and if I push each object into an array, I can sort it the way I want.
Why does .sort not work on this:
{ A:{...}, B:{...}, C:{...} }
but works on this:
[ {...}, {...}, {...} ]
It's still accessing and working with object properties in both examples, right?
Here's some code:
var query = {
"736":{
ns: 42,
pageid: 12,
lang: "en",
index: 3
},
"421":{
ns: 12,
pageid: 36,
lang: "en",
index: 4
},
"102":{
ns: 2,
pageid: 19,
lang: "en",
index: 1
}
};
var queryArr = [{ns: 42, pageid: 12, lang: "en", index: 3}, {ns: 12, pageid: 36, lang: "en", index: 4}, {ns: 2, pageid: 19, lang: "en", index: 1}];
query is an object with multiple objects in it, and queryArr is an arry with multiple objects in it. If I wanted to sort query by its index key's value, I'd have to convert it to an arry first, and then run .sort on that array, correct?
What I want to know is why. What prevents query from being sorted, but allows the objects inside queryArr to be sorted? They're both still objects right? The only difference is the outer body is an object in the first, and an array in the second - but it's still working with objects when sorting.
My sort function still referneces the index using the object property accessor:
queryArr.sort(function(i,j){
return j.index - i.index;
});
Array object is object where the positive integer keys are used:
a = [, 1]
a[.1] = .1
a[-1] = -1
console.log( a )
console.log( { ...a } )
If the order of the properties can't be guaranteed, then for example array object [0, 1] can be stored as { "0": 0, "1": 1 } or { "1": 1, "0": 0 }.
Most array looping constructs will look for the "0" property before the "1" property, so the property order doesn't really matter.
Sorting array doesn't change it's properties order, but swaps the values associated with the properties.
Arrays are a special way of sequentially storing data. In the earliest implementations, this would be done by actually storing the array objects sequentially in memory. And resorting would actually physically move the objects in memory. So in your example where you have indices 102, 421, and 736. If you translated this to an array, you would actually have an array of length 737. 0 to 101 would be undefined, then you would have your object at 102. 103 to 420 would be undefined, then object 421. Et cetera.
Good to note that in your example when you translated your object into an array, you lost your keys (102, 421, 736). They simply became (0,1,2). In your example maybe this was okay, but if you had an object with properties like width, height, length, having these replaced with simple array indices like 0,1,2 would be a pretty significant loss of information.
Objects don't work the same way at all. They are not designed to store data sequentially, but hierarchically. So you can have a key 102 that points to an object, but you don't have to have keys 0-101. And key 102 doesn't designate the "order" of the item. It's just a name, and it could just as easily be length or fred or green as 102.
This mirrors reality. For example you might have an array to store a group of people, perhaps starting in order of age. And you could re-sort the list by different properties, like alphabetical by last name, or by height, etc.
But if we look at the objects within that array, it really makes no sense to talk about the order of firstName, lastName, height, weight, age, etc. There isn't really any "order" to speak of: weight doesn't have to come before height, or vice versa. And some people may like to see Last,First, while others prefer First Last. These properties are things we mostly want to be able to access directly by name, so an array isn't really ideal. We want named properties. Hence an object. Don't be confused just because you chose numbers as your property names...they're still names, not indices.
However it is of course possible to iterate all the properties of an object, and it is even possible to control the order in which you iterate them. Traditionally in javascript this iteration was done with the for...in syntax, which goes all the way back to es1. But you couldn't control the order in which the object's keys were iterated. To control order we would have to use for...in to populate an array of keys, then sort the array, then re-loop over the array.
However, it is possible in the newer es6 javascript world to iterate in some great new ways. For example, you can use Object.keys() to get an array of all the object keys (property names), and then you could sort this array. Saves the step of populating an array with for...in.
Another more advanced possibility in es6 (which uses Object.keys) is to actually make the object itself iterable by adding a Symbol.iterator. Again, this only works in es6+.
However the Symbol.iterator gives you a lot of power. This is what it looks like:
query[Symbol.iterator] = function*() {
let properties = Object.keys(this).sort();
for(let p of properties) {
yield {key:p, value:this[p]}
}
}
Once you add this iterator to the object, now you can use things like for...of. And in this example I added a .sort(), so this would iterate over the object's properties in ascending numeric order by key: 102, 421, 736. And since we are yielding an object with key and value, we are still able to see the key value, which we would NOT have if we had just translated to an array.
for(let {key,value} of query) {
console.log(key);
}

Why can't you access this array with index?

Why does this array have a length property of 0? And as a follow up to that question, why can't you access an item in this array with an index value?
Consider the following:
var box = [];
box['material'] = 'cardboard';
box['size'] = 'small';
box.length; //0
console.log(box);
//[ material: 'cardboard', size: 'small' ]
box[0]; //undefined
However, when I do the following:
var box = [];
box['0'] = true;
box['1'] = 'cardboard';
box['2'] = 'some value';
box.length; //3
box[0];// true
Why does it output the correct length in this case, since '0' is a string and not a number and access the correct item with the 0 index?
In JavaScript, the length of an array is always one more than the largest numeric (integer) property name. Arrays can have properties whose names are not numeric, but they don't count towards the length of the array (and they are ignored in some other important situations).
Object property names are always strings, but strings that are non-negative integer values when interpreted as numbers are special in arrays. Accessing properties with numeric values works because the numbers are first converted to strings; thus box[0] and box['0'] do exactly the same thing.
when setting box['material'] it creates a property called 'material' with value 'cardboard'. this doesn't add a element to the list!
you need to use .push() or asign the index you want.
also, the '0' string is cased to a number, so obj['0'] and obj[0] is the same (not just for 0, this happens for all numbers)
I think you’re confusing objects with arrays. While arrays are indeed objects in javascript, that doesn’t help us here.
What you are trying to instantiate is an associative array with key-value pairs “material”->“cardboard”, “size”->“small”. However, such a datastructure does not exist in javascript. Array must only have integers as indexes, as in
box = [];
box[0] = 'material';
box[1] = 'someValue';
The reason why your code (which only seems to be an associative array)
box = [];
box['0'] = true;
box['1'] = 'cardboard';
works is because of implicit type conversion. Because you’ve declared box to be an array, a string is not valid in this position, so javascript quietly converts it into an integer, so you again get a plain, boring, integer-indexed array as above.
What you can do, though, is create an object with attributes. So you might, for example, state
box = {}; // box is now an object
box.size = 'small'; // box now has an attribute called “size”
// which has the (string) value “'small'”
box.material = 'cardboard';
or at one go using the notation for object literals:
box = {
size: 'small',
material: 'cardboard'
};
This is the closest you can get to associative arrays in javascript. Note that objects do not have a length. If you want to access the number of attributes they have, you have to use Object.keys(box).length. To iterate over the keys, use
for(var key in box){
console.log('value of ' + key + ' is ' + box[key])
}
Note the array-like notation here, but remember: You’re dealing with objects, not arrays, though dealing with them looks similar. Since box is of type object, you will not only get the attributes you have defined yourself, but also any attributes which are defined for every object. To get only the keys you put in there yourself, use
for (var key in box) {
if (box.hasOwnProperty(key)) {
// do stuff
}
}
If you need to add keys, i.e. attributes at runtime, use (for example)
//supposing that box was instantiated as above
var listOfBoxAttributes = ['shape', 'color', 'weight'];
for(var i in listOfBoxAttributes)
box[listOfBoxAttributes[i]] = undefined;

How does JavaScript [] really work?

I'm writing a JavaScript interpreter for extremely resource-constrained embedded devices (http://www.espruino.com), and every time I think I have implemented some bit of JavaScript correctly I realise I am wrong.
My question now is about []. How would you implement one of the most basic bits of JavaScript correctly?
I've looked through the JavaScript spec and maybe I haven't found the right bit, but I can't find a useful answer.
I had previously assumed that you effectively had two 'maps' - one for integers, and one for strings. And the array length was the value of the highest integer plus one. However this seems wrong, according to jsconsole on chrome:
var a = [];
a[5] = 42;
a["5"]; // 42
a.length; // 6
but also:
var a = [];
a["5"] = 42;
a[5]; // 42
a.length; // 6
So... great - everything is converted into a string, and the highest valued string that represents an integer is used (plus one) to get the length? Wrong.
var a = [];
a["05"] = 42;
a.length; // 0
"05" is a valid integer - even in Octal. So why does it not affect the length?
Do you have to convert the string to an integer, and then check that when converted back to a string, it matches?
Does anyone have a reference to the exact algorithm used to store and get items in an array or object? It seems like it should be very simple, but it looks like it actually isn't!
As the specs said, and was noted by others:
"A property name P (in the form of a String value) is an array index if and only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal to 2^32-1."
That's explain why in your scenario "5" is considered an array index and "05" is not:
console.log("5" === String("5" >>> 0));
// true, "5" is equal to "5", so it's an index
console.log("05" === String("05" >>> 0));
// false, "05" is not equal to "5", so it's not an index
Note: the Zero-fill right shift is the shortest way in JS to have a substitute of ToUint32, shifting a number by zero.
See MDN
It's possible to quote the JavaScript array indexes as well (e.g.,
years["2"] instead of years[2]), although it's not necessary. The 2 in
years[2] eventually gets coerced into a string by the JavaScript
engine, anyway, through an implicit toString conversion. It is for
this reason that "2" and "02" would refer to two different slots on
the years object and the following example logs true:
console.log(years["2"] != years["02"]);
So with a["5"] you are accessing the array while a["05"] sets a property on the array object.
Arrays are just objects. That means they can have additional properties which are not considered elements of the array.
If the square bracket argument is an integer, it uses it to perform an assignment to the array. Otherwise, it treats it as a string and stores it as a property on the array object.
Edit based on delnan's comment and DCoder's comment, this is how JavaScript determines if it is an appropriate index for an array (versus just a property):
http://www.ecma-international.org/ecma-262/5.1/#sec-15.4
Arrays are also objects.
By doing this
a["05"] = 5;
You are doing the same thing as:
a.05 = 5;
However, the above will result in a syntax error, as a property specified after a dot cannot start with a number.
So if you do this:
a = [];
a["05"] = 5;
you still have an empty array, but the property of a named 05 has the value 5.
The number x is an array index if and only if ToString(ToUint32(x)) is equal to x (so in case of "05" that requirement is not met).

What does [literal] in [array/object] mean?

What does the following syntax mean:
1 in [1,2,3,5]
I know it doesn't search for 1 in the array. But what does it do?
I've seen it used in loops:
for (var i in testArray)
{
}
But have also seen this used by itself. Does it mean check if the literal is a valid index in the array or object that is the other operand?
Very simply, it's an object property search:
print ('a' in {'a':1, 'b': 2}); // true
print ('c' in {'a':1, 'b': 2}); // false
Live demo.
This is subtly different to the similar use of in in for loops.
Also note that it should not be used with arrays, although this is a common error to make.
It is unspecified what properties besides numeric keys make up the internals of an array, so you should stick to the Array API (e.g. indexOf) if you're using Arrays, otherwise you'll end up with behaviour that you may not expect:
print ('length' in [1,2,3,4]); // true
literalinobject means: "Get a property literal from object."
When used in a loop, the engine will try to access all properties of the object.
1 in [1,2,3,4] doesn't check for an occurence of an element with a value of 1, but it checks whether element 1 (array[1]) exists or not.
It looks like a cheap way not to use a traditional for loop for each item in the array.
Your 2nd example is a for-each loop. It doesn't have the literal 1 (one).
It's used to iterate JavaScript objects.
This loop will iterate through each "key" in the object.
Its common usage is iterate through such objects:
var Car = { color: "blue", price: 20000 };
for (var key in Car)
console.log("Propery " + key + " of Car is: " + Car[key]);
Live test case - check Chrome/Firefox JavaScript console to see the output.
When used for plain arrays, each "key" will be the index.. for example:
var nums = [20, 15, 30]
for (var key in nums)
console.log("Propery " + key + " of array is: " + nums[key]);
Will show the keys as 0, 1 and 2. Updated fiddle for such case.
'in' operator returns true if specified property exists in specified object; used in a loop it allows you to iterate over all properties of the object

Categories

Resources