I just realized I am having a malfunction in my overall web app and it's coming down to a for loop that is reordering my object/array.
I am retrieving an associative array (object) via AJAX. I can check it's structure upon return and it is correct. For example:
48 => Value1
50 => Value2
49 => Value3
51 => Value4
But, when I loop through it, the for loop reorders the object. This is because the assoc keys are numeric. They represent numeric IDs froma database. I didn't know javascript would choke on them and try to turn them into basic array keys in numeric order. So, it spits out as:
48 => Value1
49 => Value3
50 => Value2
51 => Value4
How can I loop through the object in the order I build it in? Do I have to build the object with strings as keys? Like "ID22" replacing 22. Or is there some way to force javascript to loop in a specific order?
Thanks!
Matt
There is no way to iterate JavaScript object in specific order, because JS object is unordered.
The best way to solve this problem - create 1 object and one array to keep order:
var values = {
48: "Value1"
50: "Value2"
49: "Value3"
51: "Value4"
}
var order = [48, 50, 49, 51] //put proterty id here to keep order eg. [51, 50, 49, 48]
This is only really a problem with Chrome, they have a bug for it but it's a "Won't Fix" for the sake of internal efficiencies.
The best way I've found to get around this problem is to changing using an object:
{
48: "Value1"
50: "Value2"
49: "Value3"
51: "Value4"
}
to instead an array like so:
[
{id: 48, value: "Value1"},
{id: 50, value: "Value2"},
{id: 49, value: "Value3"},
{id: 51, value: "Value4"}
]
It ends up being much more clean than the alternative workaround (which is as you originally suggested)
I believe you're using for…in to retrieve your key and value.
As the link above says, and as it's specified in ECMAScript specs, "The mechanics and order of enumerating the properties (step 6.a in the first algorithm, step 7.a in the second) is not specified."
It means, you can't rely at all on the order you will get via for…in or Object.keys. If the order is important to you, you have to store the order's index somewhere, and sort the data by that property. You can't use a mere Object like that.
Related
I’m trying to order an object data structure by the value.
{apple: 1, pear: 85, orange: 24, blueberry: 18}
I want to achieve a reorder by value in DESC order. I don’t think there is a a method to do so, even if this object was wrapped in an Array, but wanted to check.
My other thought process was to separate the key value pair into 2 separate arrays. Then loop the value array, search for the max vale and capture the index at that point and that particular index should match at the key array. Then i can move the key/value pair into a third/separate array keeping order by max value. Then based on the index number captured, pop from both the key and value array.
If there is a better method please feel free to share?
An Object's keys are not sorted, so you will not be able to achieve what you want with just an Object.
If you use an Array, however, you can use Array.sort():
const myArray = [{apple: 1}, {pear: 85}, {orange: 24}, {blueberry: 18}]
myArray.sort((e1, e2) => Object.values(e2)[0] - Object.values(e1)[0])
console.log(myArray)
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);
}
I have a rather large JSON object being generated via PHP. It creates a PHP object out of a database, with keys that are integers, ie 1-100. These keys are not in that order though, they are in a random order, such as 55, 72, 5, 8, 14, 32, 64, etc. I then use json_encode to output the object as JSON. I then use an AJAX call to grab that JSON and store it in a variable. However, that variable has the JSON object in order 1-100, instead of the sorted order above.
Any ideas why it's doing that, and how I can fix it?
JSON objects have no specific order and are not guaranteed to keep the order you put things in. If you want keys in an order, you should put them in an array which will maintain order. If you want an ordered set of key/value pairs, you can use several different forms.
The simplest would be to just have a single array that is an alternating set of key, value:
var data = ["key1", "value1", "key2", "value2",...];
Or, you could do an array of objects:
var data = [{key: "key1", data: "value1"}, {key: "key2", data: "value2"}, {...}]
Keys within an object aren't technically ordered. That you expect them to be in a particular order is a mistake in data structure. A JSON parser will interpret numerical keys as array positions.
this
{
55: "foo",
29: "bar",
...
}
is semantically the same as:
object[55] = "foo"
object[29] = "bar"
which is:
[
...
"bar" //index 29
...
"foo" //index 55
...
]
Instead, you should separate the order and the identify of your objects:
[
{id: 55, content: "foo"},
{id: 29, content: "bar"},
...
]
I use PHP arrays with numeric keys, but they don't go from 0 to n, but can be randomly chosen (it is a script for implementing schedule events which take some specific places in the schedule. They must be associated to the right place because there can be more than one on the same place; but some places can also be empty). Such arrays are converted to JS objects (maps) and it's OK. But, if an array contains accidentally only one item with key "0", it's converted to a JS array, because it IS actually a proper array. Therefore the result is not consistent.
$array1 = [2 => "something", 3 => "something"];
becomes:
array1 = { 2: "something", 3 : "something" }; (GOOD)
but
$array2 = [0 => "something"];
becomes:
array2 = ["something"]; (BAD)
How can I force a consistent conversion?
One idea would be to add a dummy key to each array to make it associative:
$array3 = [0 => "something", "forceMap" => true];
Any better idea?
According to this site: http://de2.php.net/json_encode you just have to use the JSON_FORCE_OBJECT parameter with the json_encode() function to do this:
$json = json_encode($yourObject, JSON_FORCE_OBJECT)
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.