how Object.keys(obj) works? - javascript

As per the description of the Object.keys on MDN:
Object.keys returns an array whose elements are strings corresponding to the enumerable properties found directly upon object. The ordering of the properties is the same as that given by looping over the properties of the object manually.
It includes the following example:
// array like object with random key ordering
var an_obj = { 100: "a", 2: "b", 7: "c"};
alert(Object.keys(an_obj)); // will alert "2, 7, 100"
But as per the definition, key should be printed as 100, 2, 7 in the order they have been inserted in the object, instead of 2, 7, 100.
Please let me know, how the ordering of key happens in Object.key.

I think you might have misunderstood this:
The ordering of the properties is the same as that given by looping over the properties of the object manually.
What this means is that the order of the properties in Object.keys is the same as if you did a for-in loop.
Compare the results of these:
var an_obj = { 100: "a", 2: "b", 7: "c"};
//using object.keys...
console.log(Object.keys(an_obj));
//using a manual loop...
for(var k in an_obj) { console.log(k); }
You will find the order for both these is the same.

JavaScript objects are unordered key-value pairs. Object.keys returns an array of the keys of the object in the order they would be returned if you were to iterate over them, but it makes no guarantees over the order of the result in relation to the order of insertion.

Objects are by definition (See specification: http://www.ecma-international.org/publications/standards/Ecma-262.htm ) unordered. So no matter how you did the input, there is no guarantee of the order of the keys. If you wish to maintain the order, you should keep a seperate list with the order of the keys.

I am explaining a bit more about the difference between these :
Object.keys(obj)
for(var key in obj){
console.log(key);
}
Similarity :
Both returns keys in same order. Not a single difference between these.
Explanation:
If you have numeric keys in your object then
All numeric keys comes in natural ordering.
All other keys comes in insertion order.
If object does not contain any numeric key then insertion order will be followed by both the tricks.
Example:
var sec_obj = {0:"0", "d":23, "r": "a", "a": "b", "7": "c","z":23, 23:23};
//using object.keys...
console.log(Object.keys(sec_obj));
//using a manual loop...
for(var k in sec_obj) { console.log(k); }
Result
["0", "7", "23", "d", "r", "a", "z"]
0
7
23
d
r
a
z

It’s important to mention the limitations of Object.keys, as MDN article does. It recommends to use getOwnPropertyNames. MDN articles also show browser support.

Related

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);
}

Does JSON.parse() really sort properties when the key names have numeric values?

There's a number of posts here about this issue, and they all contain a lot of assertions that can be summarized like this:
Object properties are never guaranteed to be ordered in any way.
JSON.parse() never sorts properties in any way.
Obviously we tend to have no doubt about #1 above, so we may reasonably expect that, for any operation, properties are processed merely in the order they appear.
[edit, following the #Bergi's comment: or at least they should appear in a random order]
Then from that we might especially infer that #2 should be true.
But look at this snippet:
(BTW note: to show the results, snippets below don't use console.log() which may itself change order of the output. Instead objects are iterated by for (key in obj) and the output displayed in the document)
var inputs = [
'{"c": "C", "a": "A", "b": "B"}',
'{"3": "C", "1": "A", "2": "B"}',
'{"c": "C", "a": "A", "3": "C", "1": "A", "b": "B", "2": "B"}'
];
for (var i in inputs) {
var json = inputs[i],
parsed = JSON.parse(json),
output = [];
for (var j in parsed) {
output.push(j + ': ' + parsed[j]);
}
document.write(`JSON: ${json}<br />Parsed: ${output.join(', ')})<hr />`);
}
It shows that, given a JSON string having unordered keys:
When the input has keys with non-numeric values, the parsed object has its properties in the same order than in the input. This is consistent with the #2 assumption above.
Conversely when the input has keys with numeric values (though they're strings, so not firing parse error), the parsed object has its properties sorted. This now contradicts the #2 assumption.
More: when there are mixed numeric and non-numeric key values, first appear the numeric properties sorted, then the non-numeric properties in their original order.
From that I was first tempted to conclude that actually there would be a (non-documented?) feature, so JSON.parse() works following the "rules" exposed above.
But I had the idea to look further, so the snippet below now shows how ordered are the properties of a merely coded object:
var objects = [
[
'{"c": "C", "a": "A", "b": "B"}',
{"c": "C", "a": "A", "b": "B"}
],
[
'{"3": "C", "1": "A", "2": "B"}',
{"3": "C", "1": "A", "2": "B"}
],
[
'{"c": "C", "a": "A", "3": "C", "1": "A", "b": "B", "2": "B"}',
{"c": "C", "a": "A", "3": "C", "1": "A", "b": "B", "2": "B"}
]
];
for (var i in objects) {
var object = objects[i],
output = [];
for (var j in object[1]) {
output.push(j + ': ' + object[1][j]);
}
document.write(`Code: ${object[0]}<br />Object: ${output.join(', ')}<hr />`);
}
It results in analogue observations, i.e. whichever order they're coded, properties are stored following the 3rd rule above:
numerically named properties are all put first, sorted
other properties are set next, ordered as coded
So it means that JSON.parse() is not involved: in fact it seems to be a fundamental process of object building.
Again this appears not documented, at least as far I could find.
Any clue for a real, authoritative, rule?
[Edit, thanks to #Oriol's answer] It actually appears that, synthetically:
This behaviour conforms to an ECMA specification rule.
This rule should apply to all methods where a specific order is guaranteed but is optional for other cases.
However it seems that modern browsers all choose to apply the rule whatever method is involved, hence the apparent contradiction.
The properties of an object have no order, so JSON.parse can't sort them. However, when you list or enumerate the properties of an object, the order may be well-defined or not.
Not necessarily for for...in loops nor Object.keys
As fully explained in Does ES6 introduce a well-defined order of enumeration for object properties?, the spec says
The mechanics and order of enumerating the properties is not specified
But yes for OrdinaryOwnPropertyKeys
Objects have an internal [[OwnPropertyKeys]] method, which is used for example by Object.getOwnPropertyNames and Object.getOwnPropertySymbols.
In the case of ordinary objects, that method uses the OrdinaryGetOwnProperty abstract operation, which returns properties in a well-defined order:
When the abstract operation OrdinaryOwnPropertyKeys is called with
Object O, the following steps are taken:
Let keys be a new empty List.
For each own property key P of O that is an integer index, in
ascending numeric index order
Add P as the last element of keys.
For each own property key P of O that is a String but is not
an integer index, in ascending chronological order of property creation
Add P as the last element of keys.
For each own property key P of O that is a Symbol, in ascending chronological order of property creation
Add P as the last element of keys.
Return keys.
Therefore, since an order is required by OrdinaryOwnPropertyKeys, implementations may decide to internally store the properties in that order, and use it too when enumerating. That's what you observed, but you can't rely on it.
Also be aware non-ordinary objects (e.g. proxy objects) may have another [[OwnPropertyKeys]] internal method, so even when using Object.getOwnPropertyNames the order could still be different.
so we may reasonably expect that, for any operation, properties are processed merely in the order they appear
That's where the flaw in the reasoning lies. Given that object properties aren't guaranteed to be ordered, we have to assume that any operation processes properties in any order that it sees fit.
And in fact engines evolved in a way that treat integer properties specially - they're like array indices, and are stored in a more efficient format than a lookup table.

Count length of array of array using javascript

I have this array and I want to count total numbers into this, can anyone please let me know how can i achieve this using pure javascript
var total = {
"numbers":{
"1":"9897877884",
"2":"9867543234",
"3":"7898879900",
"4":"1234567890"
}
}
You could use Object.keys(total.numbers).length. The first part returns an array with the keys and the second gets the length of it.
var total = { "numbers": { "1": "9897877884", "2": "9867543234", "3": "7898879900", "4": "1234567890" } };
console.log(Object.keys(total.numbers).length);
Object.keys()
The Object.keys() method returns an array of a given object's own enumerable properties, in the same order as that provided by a for...in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well).
Array#length
The length property represents an unsigned, 32-bit integer that is always numerically greater than the highest index in the array.

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

Is the order of elements in a JSON list preserved?

I've noticed the order of elements in a JSON object not being the original order.
What about the elements of JSON lists? Is their order maintained?
Yes, the order of elements in JSON arrays is preserved. From RFC 7159 -The JavaScript Object Notation (JSON) Data Interchange Format
(emphasis mine):
An object is an unordered collection of zero or more name/value
pairs, where a name is a string and a value is a string, number,
boolean, null, object, or array.
An array is an ordered sequence of zero or more values.
The terms "object" and "array" come from the conventions of
JavaScript.
Some implementations do also preserve the order of JSON objects as well, but this is not guaranteed.
The order of elements in an array ([]) is maintained. The order of elements (name:value pairs) in an "object" ({}) is not, and it's usual for them to be "jumbled", if not by the JSON formatter/parser itself then by the language-specific objects (Dictionary, NSDictionary, Hashtable, etc) that are used as an internal representation.
Practically speaking, if the keys were of type NaN, the browser will not change the order.
The following script will output "One", "Two", "Three":
var foo={"3":"Three", "1":"One", "2":"Two"};
for(bar in foo) {
alert(foo[bar]);
}
Whereas the following script will output "Three", "One", "Two":
var foo={"#3":"Three", "#1":"One", "#2":"Two"};
for(bar in foo) {
alert(foo[bar]);
}
Some JavaScript engines keep keys in insertion order. V8, for instance, keeps all keys in insertion order except for keys that can be parsed as unsigned 32-bit integers.
This means that if you run either of the following:
var animals = {};
animals['dog'] = true;
animals['bear'] = true;
animals['monkey'] = true;
for (var animal in animals) {
if (animals.hasOwnProperty(animal)) {
$('<li>').text(animal).appendTo('#animals');
}
}
var animals = JSON.parse('{ "dog": true, "bear": true, "monkey": true }');
for (var animal in animals) {
$('<li>').text(animal).appendTo('#animals');
}
You'll consistently get dog, bear, and monkey in that order, on Chrome, which uses V8. Node.js also uses V8. This will hold true even if you have thousands of items. YMMV with other JavaScript engines.
Demo here and here.
"Is the order of elements in a JSON list maintained?" is not a good question. You need to ask "Is the order of elements in a JSON list maintained when doing [...] ?"
As Felix King pointed out, JSON is a textual data format. It doesn't mutate without a reason. Do not confuse a JSON string with a (JavaScript) object.
You're probably talking about operations like JSON.stringify(JSON.parse(...)). Now the answer is: It depends on the implementation. 99%* of JSON parsers do not maintain the order of objects, and do maintain the order of arrays, but you might as well use JSON to store something like
{
"son": "David",
"daughter": "Julia",
"son": "Tom",
"daughter": "Clara"
}
and use a parser that maintains order of objects.
*probably even more :)

Categories

Resources