How to keep order from json.parse? [duplicate] - javascript

I have some data which I originally stored in a generic Javascript object, with the ID as a key:
{
"7": {"id":"7","name":"Hello"},
"3": {"id":"3","name":"World"},
...
}
However, I discovered that browsers do not guarantee a particular object order when looping through them, so in the above "3" would come before "7". I switched to using an array format like this:
[
{"id":"7","name":"Hello"},
{"id":"3","name":"World"},
...
]
Now, I can loop in the correct order but cannot do fast lookups, e.g. data["3"] without having to loop through the array.
Is there a good way to combine both approaches? I would rather avoid using a separate object for each format, because the object is pretty large (hundreds of elements).

I have run across this problem as well. A solution is to keep an ordered array of keys in addition to the original object.
var objects = {
"7": {"id":"7","name":"Hello"},
"3": {"id":"3","name":"World"},
...
}
var order = [ "3", "7", ... ];
Now if you want the second element you can do this lookup:
var second_object = objects[order[1]];
The ECMA standard does not say anything about the order of the elements in an object. And specifically Chrome reorders the keys when they look like numbers.
Example:
var example = {
"a": "a",
"b": "b",
"1": "1",
"2": "2"
};
if you print this in Chrome will get something like:
{
1: "1",
2: "2",
"a": "a",
"b": "b"
};
It's a little sour .. but life.
You could use the solution Andy linked as well, basically wrapping these two together in one object.
An alternative that I use a lot is a custom map function that allows you to specify the order in which the object is traversed. Typically you will do sorting when you're printing your data to the user so while you loop and create your table rows (for instance) your iterator will pass the rows in the order your sort function specifies. I thought it was a nice idea :)
The signature looks like:
function map(object, callback, sort_function);
Example usage:
map(object, function (row) {
table.add_row(row.header, row.value);
}, function (key1, key2) {
return object[key1] - object[key2];
});

Rather than coding your own, there are off-the-shelf libraries available to provide "as provided" JSON parsing or "consistently sorted" JSON printing for display.
You might well consider either of these:
The 'json-order' package offers parsing, formatting & pretty-printing with stable ordering. This is based on having ordered input.
The 'fast-json-stable-stringify' package offers deterministic formatting based on sorting.

Related

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.

JSON.stringify gives wrong index structure

I'm trying to use JSON.strigify() on my object (see screenshot).
However I get result which I'm not expecting, object indexes are in wrong order.
Full stringified json, you can see here: http://vpaste.net/LqNlq
As you can see first index is 9:0, but not 8:0 as expected.
What's the problem here ?
The keys of Objects in javascript aren't guaranteed to be in any order.
You should make it an Array of Objects instead to preserve order.
e.g.
{
"1": [
{ "key": "8:0", ... },
{ "key": "8:30", ... },
...
],
"2": ...
}
This should also be the same structure if you expect your top level keys ("1", "2", etc.) to be iterated over in order.

Getting the key and value of a javascript object

Given an javascript object:
myList = {
"1": "Winter",
"2": "Spring",
"3": "Summer",
"4": "Fall"
}
and supposing that the variable season contains the first item, how do I access "1" and/or "Winter"?
Edit: myList['1'] is not the answer I am looking for because I want to access either "1" or "Winter" from the season variable. It is not a duplicate of the marked question.
Instead, I want to do something like:
for(var season in myList) {
foo( season.key ); // should be "1" on the first pass
bar( sesson.val ); // should be "Winter" on the first pass
}
I've seen somewhere that you can do an inner loop to get the key and value. Is there some other way aside from doing that?
Libraries like lodash and underscore were made to help with these kinds of problems. You can use the lodash#forEach function to iterate over the values (and keys if applicable) in a collection (array or object).
Check out: https://lodash.com/docs#forEach
So with your example you could do something like:
_.forEach(myList, function(value, key) {
foo(key); // 1
bar(value); // "Winter"
});
You can simply get the value of the key by:
myList['1']
To get the key from the value there is no standard method available in javascript. You would have to write your own method for this.

How to keep an Javascript object/array ordered while also maintaining key lookups?

I have some data which I originally stored in a generic Javascript object, with the ID as a key:
{
"7": {"id":"7","name":"Hello"},
"3": {"id":"3","name":"World"},
...
}
However, I discovered that browsers do not guarantee a particular object order when looping through them, so in the above "3" would come before "7". I switched to using an array format like this:
[
{"id":"7","name":"Hello"},
{"id":"3","name":"World"},
...
]
Now, I can loop in the correct order but cannot do fast lookups, e.g. data["3"] without having to loop through the array.
Is there a good way to combine both approaches? I would rather avoid using a separate object for each format, because the object is pretty large (hundreds of elements).
I have run across this problem as well. A solution is to keep an ordered array of keys in addition to the original object.
var objects = {
"7": {"id":"7","name":"Hello"},
"3": {"id":"3","name":"World"},
...
}
var order = [ "3", "7", ... ];
Now if you want the second element you can do this lookup:
var second_object = objects[order[1]];
The ECMA standard does not say anything about the order of the elements in an object. And specifically Chrome reorders the keys when they look like numbers.
Example:
var example = {
"a": "a",
"b": "b",
"1": "1",
"2": "2"
};
if you print this in Chrome will get something like:
{
1: "1",
2: "2",
"a": "a",
"b": "b"
};
It's a little sour .. but life.
You could use the solution Andy linked as well, basically wrapping these two together in one object.
An alternative that I use a lot is a custom map function that allows you to specify the order in which the object is traversed. Typically you will do sorting when you're printing your data to the user so while you loop and create your table rows (for instance) your iterator will pass the rows in the order your sort function specifies. I thought it was a nice idea :)
The signature looks like:
function map(object, callback, sort_function);
Example usage:
map(object, function (row) {
table.add_row(row.header, row.value);
}, function (key1, key2) {
return object[key1] - object[key2];
});
Rather than coding your own, there are off-the-shelf libraries available to provide "as provided" JSON parsing or "consistently sorted" JSON printing for display.
You might well consider either of these:
The 'json-order' package offers parsing, formatting & pretty-printing with stable ordering. This is based on having ordered input.
The 'fast-json-stable-stringify' package offers deterministic formatting based on sorting.

reverse json javascript

Is there an inexpensive way to reverse:
{
"10": "..."
"11": "...",
"12": "...",
"13": "...",
"14": "...",
}
so that I get:
{
"14": "...",
"13": "...",
"12": "..."
"11": "...",
"10": "...",
}
reverse() doesn't seem to work on json objects. The only way I can think of is to loop through all the elements and create an array. feels like there should be a better way.
Edit: thanks for all the help UPDATE:
What about let's say if each key has chronological data. When I use $.each on the object, it runs through the objects from top to bottom, I didn't realize that was unreliable.
Here's what I'm trying to do:
$.each(object, function (key, value) {
function foo (key, value);
});
I want to not run foo on all but the last 3 pairs, that is I only want to use the last 3 pairs. I figured if I can reverse them I can just run the first three and stop.
Is there any way I can just do the last 3? If the last 3 ordering is unreliable, is there a safer way to grab the last 3. The last 3 will have the largest numerical keys.
Thanks.
Edit 2:
I'm basically deciding finally to do the manipulations on the server side. I'm reorganizing my database so that the relevant subdocuments are now full on documents that could be queried with mongodb. Thanks.
Javascript associative arrays are unordered. You cannot depend on the properties being in any particular order.
From Mozilla Developer Network:
Although ECMAScript makes iteration
order of objects
implementation-dependent, it may
appear that all major browsers support
an iteration order based on the
earliest added property coming first
(at least for properties not on the
prototype). However, in the case of
Internet Explorer, when one uses
delete on a property, some confusing
behavior results, preventing other
browsers from using simple objects
like object literals as ordered
associative arrays. In Explorer, while
the property value is indeed set to
undefined, if one later adds back a
property with the same name, the
property will be iterated in its old
position--not at the end of the
iteration sequence as one might expect
after having deleted the property and
then added it back.
So if you want to simulate an ordered
associative array in a cross-browser
environment, you are forced to either
use two separate arrays (one for the
keys and the other for the values), or
build an array of single-property
objects, etc.
Use this on json objects arrays
jsonObjectArray.reverse();
$.each(jsonObjectArray, function(i, item) {
//do something with the item
});
Hope this helps
This might help. Get all the keys from the json objects into an array, which you can sort.
var a = { 1 : 'x', 3 : 'y', 2 : 'z' };
var keys = []
for (i in a) { keys.push(i); }
keys.sort();
then you can use reverse() and slice() to just iterate over the keys you need.
$.each(keys, function(idx, key) {
// do whatever with a[key]
});
Follow the json in reverse order,
for(json.length;i>=0;i--)
{
console.log(json.[i]);
}

Categories

Resources