How do you stop Chrome and Opera sorting JSON objects by Index ASC?
Asked Answered
H

18

67

I've got a problem.

Using ajax I sent a correctly formed JSON object using:

            $.ajax({
                type: "POST", 
                url: SITE_URL+'/data.php',
                dataType: "json",
                data: { ajax: 1 },
                success: function(data) {
                    console.log(data);
                }
            });

However, Opera and Chrome, although receiving the same object, print out the object in an incorrect order, it seems like they both perform a sort by ID number instead of just leaving it alone!

Is there a way to stop this auto sort?

Edit, after finding out it is a sort by index number I'm thinking the best method might be to not use the index for storing the object_id and instead store the id number which I want to order the object by.

However I would still like to know if there is a way to stop the sort.

Thank you

Edit2, I'll just like to note that I'm going to work on a different way of doing this, as I feel like I'm abusing objects with this method. However I'd still like to understand why Opera and Chrome feel it is their right to change the order of my objects IDs:

The problem would be me trying to save processing power, lets say we have people with an ID,

1.John, 2.Frank and 3.Sally. However each of these people have a hight property set (and other things). 1.John.180, 2.Frank.220, 3.Sally.150. To save on processing, my I request the result of people be sorted by their height so I get an array of 2, 1, 3 with their other properties. I JSON this array and send it to the browser.

Now FF will keep the new order People[1] would still be John but in a For n as person loop they'll be out of order.

If I can't get around this I'll just have to not bother sorting at the SQL stage and add extra looping and sorting into an array in the JS stage although I wanted to avoid more stress on the browser as its already a Js heavy page.

Many thanks

Hardness answered 16/2, 2011 at 18:44 Comment(6)
Can you post the order differences between browsers?Kv
Edited with the info. FF and IE take the object as it is given, where opera and chrome do a sort by ID on the object without being asked...Hardness
A colleague of mine at Opera replied "curiosity killed the cat" but indeed, I think it might be things in handling the object at the code level. Not that important. I guess engineers preferences. :)Bailar
Thanks karlcow, indeed I seemed to have just been abusing the code and opera and chrome punished me for it :PHardness
If someone solve this, please report, I'm having this same problem with all browsersLabanna
Hey how can i resolve this error i have a same problemPalatable
H
21

Different browsers handle objects in different ways, my fault was to try and use the order I built an object as a reference where I shouldn't.

Hardness answered 23/2, 2011 at 11:7 Comment(1)
They used to be in the order inserted, i ended up putting a '+' in front of the number to prevent sorting, then use parseInt to convert it to a number, on the other hand you can now sort numbers by inserting it into a object (Firefox, chrome, IE11, and Edge behave like this today)Sudbury
G
27

Had same problem, followed dmc's solution but just added a space in front of the int value to make it a string.

The advantage of using a space rather than another non numeric character is that the subsequently POSTed value can be used directly in a mySQL search clause without having to remove it again.

Gamosepalous answered 7/9, 2012 at 16:15 Comment(3)
Furthermore, in the for/each loop, you can do a trim and get rid of the space, and it still works.Hesperides
(2016) I had the same problem and solved it exactly the same way before finding this post ... I added space after the ID though, tested in FF, Chrome, IE and EdgeInflation
Was looking for a simple solution that just worked. This is it. 2017 works in all browsers. Very nice. Very logical/easy.Enos
H
21

Different browsers handle objects in different ways, my fault was to try and use the order I built an object as a reference where I shouldn't.

Hardness answered 23/2, 2011 at 11:7 Comment(1)
They used to be in the order inserted, i ended up putting a '+' in front of the number to prevent sorting, then use parseInt to convert it to a number, on the other hand you can now sort numbers by inserting it into a object (Firefox, chrome, IE11, and Edge behave like this today)Sudbury
S
20

Changing integer to string didn't work for me (Chrome, jQuery 1.7.1). So to keep the order (yes, it's object abusing), I changed this:

optionValues0 = {"4321": "option 1", "1234": "option 2"};

to this

optionValues0 = {"1": {id: "4321", value: "option 1"}, "2": {id: "1234", value: "option 2"}};
Studious answered 21/9, 2012 at 8:32 Comment(0)
G
14

Unless that JSON is an array, rather than an object, there is no standard that says it has to be in a certain order. However, this shouldn't be a problem since you don't need to iterate through the object to get the data, you can simply refer to the property.

Grigson answered 16/2, 2011 at 18:55 Comment(3)
I'll update my answer with what I was using it for, it is prob me abusing objects tbh but I'll do it anywayHardness
as you can see i do iterate through the object to get a predefined order. But as I've said before, this was me cheating so the JS demand is much lighter. I'm just going to re-do my method of dealing with this. I'd still like to know the question though: "Why do Opera and Chrome reorder an object by objects reference-id"Hardness
I need the right order because i am displaying the items in accordance to my mysql result orderImperception
S
6

Some browsers will sort keys which are int, on the other hand it's very easy to change that to string and later revert, mine solution was to add "i" to key, that made a trick. Works perfectly for each browsers :) Hope that helps :)

Seasickness answered 21/6, 2012 at 10:8 Comment(0)
C
5

I had the same "problem" and did not want to go back and change too much in the code. Figured out that at least Google Chrome only re-sorts numeric indexes. As long as the index is considered a string it will show up in the "intended" order. Could someone please verify this on Opera?

Cobham answered 23/2, 2011 at 14:16 Comment(2)
Thanks for your answer! This was the quickest fix for me - converting indexes to strings in the array before creating the json.. this preserved the order (in chrome anyway, haven't verified Opera). However before leaving this as a permanent solution, I'd like to know if it will work in all browsers...Benedictine
I checked Opera, Firefox, IE. They all react like Chrome when indexes are strings.Cobham
W
2

None of the solutions worked for me (Aug 2017). Even if I used a string Chrome and Safari (didnt test other browsers) were still sorting based on the string. What I did instead was concatenate my integer key with value string. e.g

result[string_value + str(integer_key)] = string_value

This way the string is first and the browser is free to sort. Then I used some simple logic to separate the key from the value.

Hope this helps.

Wellfound answered 1/8, 2017 at 19:44 Comment(0)
I
2

I had the same issue with chrome, so frustrating. I send it a certain way, why can't chrome just take my word for it? I get that there's no "standard" way, but if that's the case, why impose your own order??

Anyway, I handled it by turning my objects into arrays, so that

{ 0: "", 2: "Frame", 1: "Masonry" }

became

[[ 0, "" ], [ 2, "Frame" ], [ 1, "Masonry" ]]

Had to change some javascript, but it ended up working perfectly.

Irrespirable answered 16/2, 2018 at 13:12 Comment(0)
N
1

Seems the best way is to avoid associative arrays at all. When you want to send an associate array simply send it as two separate arrays - one of keys and one of values. Here's the PHP code to do that:

    $arWrapper = array();
    $arWrapper['k'] = array_keys($arChoices);
    $arWrapper['v'] = array_values($arChoices);
    $json = json_encode($arWrapper);

and the simple JavaScript code to do whatever you'd like with it

            for (i=0; i < data['k'].length; i++) {
                console.log('key:' + data['k'][i] + ' val:' + data['v'][i]);
            }

I had a similar issue and posted on jQuery: $.getJSON sorting the data on Chrome / IE?

Nourishing answered 29/2, 2012 at 4:28 Comment(0)
P
1

You need to trick Google Chrome (and others) into thinking you have a string and not a number. Changing the JSON to {"2423":'abc', "2555":'xyz'} will not work.

I had to use "_2423" and "_2555" as indexes in my object to get it to work.

Further, debugging the output to console.log or using for x in y gave different results as to _.each method (see the underscore framework).

You can see my test/proof in JSFiddle with "_" vs no prefix and the for x in y loop: http://jsfiddle.net/3f9jugtg/1/

Peralta answered 25/11, 2012 at 22:24 Comment(2)
It maybe worked back in 2012, but as I can see in 2015, Chrome sort the props even if it is strings, so prefixing won't work.Caroncarotene
Actually it did work. I tested in JSFilddle and in Chrome Web Inspector. I've added the link to my answer.Peralta
M
1

I resolved this problem using this code:

$('.edit_district').editable("/moderator/sale/edit_sale/", {
    data   : " {'_7':'10 street', '_9':'9 street', '_11':'park'}",
    type   : 'select',
    submit    : 'OK',
    onblur : 'submit'
});

and in script.php:

...
case "id_district":

$district = Districts::GetDistrictByID(Database::getInstance(), (int)substr($value,1));

if($district instanceof District){
    $data["id_district"] = $district->id_district;
    echo $district->title;
}

break;

...
Manolo answered 19/8, 2013 at 9:9 Comment(0)
U
0

Make your object identifier in the JSON string parameter, it works without automatic sorting.

Unicellular answered 18/2, 2011 at 11:21 Comment(0)
W
0

There are some cases where you will need to use the record id to group things in a post process loop. Putting quotes around the key value will work. Or you can use array_merge on the final array which resets the keys.

$processed_array = array(
  235=>array("id"=>235,"name"=>"Something"),
  27=>array("id"=>27,"name"=>"Something")
);

array_merge($processed_array);

A print_r($processed_array); now returns the following:

$processed_array = array(
  0=>array("id"=>235,"name"=>"Something"),
  1=>array("id"=>27,"name"=>"Something")
);
Wherefrom answered 2/3, 2012 at 15:15 Comment(1)
array_merge() does not modify by reference; you must use its return value. This answer is misleading.Azpurua
R
0

I had the same issue and took me a while to work out what was even causing it, what a pain. I ended up removing ID from the index and used array_push, instead of being:

$section[$s_id]['Type'] = $booktype;

etc. I changed it to

array_push($sections, array('ID'=>$s_id, 
                            'CourseID'=>$courseid, 
                            'SectionName'=>$secname, 
                            'Order'=>$order, 
                            'Created'=>$created, 
                            'Description'=>$desc
                            ));

Which allowed me to keep the ordering from PHP since the new indexs where already in order (0,1,2,3 etc as opposed to 112, 56, 411 etc)

Rothschild answered 7/2, 2016 at 11:21 Comment(0)
I
0

One trick that can help is as follow:

You have to add "i" to key while you prepare your data in for loop, that made a trick. Works perfectly for each browsers.

for Example :

 for(var i=1;i<4;i++){

 data[''+i+''+your_random_generatedId+'']  = {"questionText":"","answer":"","options":"",noOfOptions:""};

 console.log(data[''+i+''+your_random_generatedId+'']); // This prints the log of each object inside data 

 }

 console.log(data); // This will print the Object without sorting

Hence your Object will be as follow:

Object {156674: Object, 201705: Object, 329709: Object}

hope this helps :)

Immethodical answered 26/3, 2018 at 7:7 Comment(0)
O
0

I had the same issue, when i added another region and needed to have it sorted a specific way. I added a "sort" field in my database table and the select sorted it as i expected, the raw JSON was as expected, but the select field i populated the data into via AJAX still sorted it by the key (region_id)? My problem was that region_id was an integer, and i solved it by encapsulate the key making it a string instead:

  while($row = $result->fetch_array()) {
    $this->regions["'".$row['region_id']."'"] = $row['region'];
  }

old code was:

  while($row = $result->fetch_array()) {
    $this->regions[$row['region_id']] = $row['region'];
  }

Much more elegant than adding all sorts of chars to the key, just to replace it in the other end.

I hope this helps others to get a cleaner code :-)

Overcautious answered 5/1, 2020 at 22:56 Comment(0)
D
0

In php we had a response with this structure:

{
  "12198882": {
    "orderId": "12198882",
    "orderDate": "2021-10-25 15:40:40",
    "total": "167.91",
    "currency": "EUR"
  },
  "12198824": {
     "orderId": "12198824",
     "orderDate": "2021-10-25 15:35:30",
     "total": "110.00",
     "currency": "EUR"
  }
}

And by following Michail's answer we managed to give a response with this new structure instead:

[
  {
    "orderId": "12198882",
    "orderDate": "2021-10-25 15:40:40",
     "total": "167.91",
     "currency": "EUR"
  },
  {
    "orderId": "12198824",
    "orderDate": "2021-10-25 15:35:30",
    "total": "110.00",
    "currency": "EUR"
  }
]

Simply by calling array_flatten() on the value of the previous response, like this:

array_flatten($service->getOrders())

And this solved the issue.

Demography answered 27/10, 2021 at 7:12 Comment(0)
T
-1

When you pass the data object to console.log, JavaScript will end up calling the toString() method for the data object. Since you don't like the default format of data's toString() method, you'll have to do the work yourself.

You can dump the object's fields yourself if you want that type of control - you'd have to get the keys/fields and sort them and then build the string version yourself.

something like:

for (field in obj)
    add field to array

sort array into whatever order you want

for (field in array)
    get value of field from obj
    create string of field : value, append to main string

console.log(main string)
Tiflis answered 16/2, 2011 at 19:47 Comment(1)
actually I'm using the For n as index loopHardness

© 2022 - 2024 — McMap. All rights reserved.