1

I apologize there are similar threads but I can't solve this issue. I have a JSON object that contains keys and values (representing file IDs and file names). Since a JS object cannot be sorted, I need to convert to an array, sort by value (i.e., file name) NOT by key (i.e., file ID). I can accomplish all this except when converting to array, I am losing my keys/file IDs (e.g., 110, 111, 112) and they are replaced with the default array keys (0,1,2, etc.).

// Assign object to a var
$obj_win_top_get_files = window.top.<?php echo $_GET['files']; ?>;

Looking at the object via console.log,

console.log('checked_boxes', $obj_win_top_get_files);

I see:

Object {110: "013_904_general.docx", 111: "013_902_info.docx", 112: "013_120_list.docx"}

// Sort JSON object by file name ("value") rather than by id ("key")
// Create an array first since JS object is NOT sortable
arr_ids_filenames = new Array(); 

// Loop thru JS object to populate new array so it can be subsequently sorted
$.each($obj_win_top_get_files, function (key, value) {
    console.log(key, value); 
    // Populate array
    arr_ids_filenames[key] = value;
});

// Sort array by values
arr_ids_filenames.sort();
// THIS IS WHERE I AM LOSING THE FILE IDs (keys)
$.each(arr_ids_filenames, function (key, value) {
    // Array may contain keys/IDs with no values/file names so make sure there is a value
    if(value){
        console.log(key, value); 
        $the_ul.append('<li id="chk_file_' + key + '">' + value + '</li>');
    }
});

Everything works except they keys are not the file IDs (110,111,112), they are the default array keys (0,1,2). It is something in the $.each() that I am not doing correctly. I am close, but have not been able to solve this one. Any recommendations will be greatly appreciated.

4
  • Printing $_GET['files'] without any sanity checks is a REALLY bad idea. XSS, CSRF etc Commented Nov 18, 2015 at 22:36
  • As you said, you can't sort objects. If you make it an array with objects in PHP, you can sort it on sub property in JS, using Array.sort Commented Nov 18, 2015 at 22:38
  • Thx for the feedback Rudie. The GET var is being sent via AJAX from one of my other pages. Commented Nov 18, 2015 at 22:45
  • You don't know how the GET var is being sent, that's the thing with hackers, they don't do what you want. If your site has sessions/cookies, they will be hijacked. Always html/js encode everything when printing. Never ever trust input. Commented Nov 19, 2015 at 18:17

3 Answers 3

2

Create an array of arrays with 2 items in the array. The key and the object. Now you have an array like:

[
  [originalKey, obj],
  [originalKey, obj],
  [originalKey, obj]
]

then use a custom sort function:

arr_ids_filenames.sort(function(a,b){ return a[1] - b[1] });

(NOTE: I'm assuming your objects are just integers, if not do this for sorting)

arr_ids_filenames.sort(function(a, b) {
  a = a[1]; // now a points to object 1
  b = b[1]; // now b points to object 2
  if (a is less than b by some ordering criterion) {
    return -1;
  }
  if (a is greater than b by the ordering criterion) {
    return 1;
  }
  // a must be equal to b
  return 0;
});

all together:

// Sort JSON object by file name ("value") rather than by id ("key")
// Create an array first since JS object is NOT sortable
arr_ids_filenames = new Array(); 

// Loop thru JS object to populate new array so it can be subsequently sorted
var i = 0;
$.each($obj_win_top_get_files, function (key, value) {
    console.log(key, value); 
    // Populate array
    arr_ids_filenames[i++] = [key,value];
});

// Sort array by values
arr_ids_filenames.sort(function(a,b){ return a[1] - b[1] });
// THIS IS WHERE I AM LOSING THE FILE IDs (keys)
$.each(arr_ids_filenames, function (i, v) {
    // Array may contain keys/IDs with no values/file names so make sure there is a value
    if(v[0]){
        var key = v[0];
        var value = v[1];
        $the_ul.append('<li id="chk_file_' + key + '">' + value + '</li>');
    }
});
Sign up to request clarification or add additional context in comments.

2 Comments

b[2] should be b[1].
@Macmee nice solution, +1.
0

UPDATED SOLUTION:

The original solution I provided does not work in IE11. Console was returning a "SCRIPT1003" error which, upon further research, led me to this SO question: JavaScript in IE11 giving me script error 1003

IE11 doesn't support ES6 key-pair shorthand. Taking this into account, the updated code should look like this:

var arr_ids_filenames  = [];

// convert your file list into an array of objects
$.each($obj_win_top_get_files, function(key, name) {
    // use key:pair format
    arr_ids_filenames.push({
        key: key, 
        name: name
    });
});

// sort by file name (and not by key/id)
arr_ids_filenames.sort(function(a, b) {
    return ((a.name < b.name) ? -1 : ((a.name > b.name) ? 1 : 0));
});

// add items to list   
$.each(arr_ids_filenames, function(index, file) {
    $the_ul.append('<li id="chk_file_' + file.key + '">' + file.name + '</li>')
});

See the code in action: https://jsfiddle.net/7y7qps88/53/

5 Comments

Thx dhira! This is the solution! Thank you so much.
Awesome! Glad to be of assistance.
First, I really appreciate your posts and solution to my issue. Unfortunately, the code and even your jsfiddle only work in Chrome, In IE 11, the list elements are not rendered and the console reveals a "script5 access denied" error. Any recommendations are appreciated.
@12AX7 see my updated post for the IE fix. Tested this is IE11 on Windows 8. Let me know if you encounter any other issues.
Thank you @Skippr! That is the solution for making it work in IE 11. I appreciate your time and expertise!
0

By adding an object to an array at an index greater than the length of the array (which is what you are doing with arr_ids_filenames[key] = value) all previous (before that index) uninitialized elements get initialized to undefined.

For instance, doing this to an empty array:

arr_ids_filenames[110] = 'ANYSTRING'

You get:

arr_ids_filenames[0] = undefined;
arr_ids_filenames[1] = undefined;
// ...
arr_ids_filenames[110] = 'ANYSTRING';

Now after you sort, since:

 'ANYSTRING' > undefined // is false
 'ANYSTRING' < undefined // is false

Your value is the smallest value and thus gets stored at index 0 with all undefineds after it. All your next storage attempts follow this pattern.

Therefore, you end up with an array of 112 elements, with your strings at the first 3 positions. One way to solve this is to create an object:

var file = {
    id: '',
    name: ''
};

And then, for each file, insert that object into a new array:

// Loop thru JS object to populate new array so it can be subsequently sorted
$.each($obj_win_top_get_files, function (key, value) {
    console.log(key, value); 
    // Populate array
    var file = {
        id: key,
        name: value
    };
    arr_ids_filenames.push(file);
});

Sort the array by file name (by using a comparator):

arr_ids_filenames.sort(function(a, b) { return a.name < b.name; });

And finally insert them in the DOM:

$.each(arr_ids_filenames, function(idx, file) {
  // Array may contain keys/IDs with no values/file names so make sure there is a value
  if (file.name) {
    var key = file.id;
    var value = file.name;
    $the_ul.append('<li id="chk_file_' + key + '">' + value + '</li>');
  }
});

Here's a running example:

var $obj_win_top_get_files = {
  110: "013_904_general.docx",
  111: "013_902_info.docx",
  112: "013_120_list.docx"
}

var arr_ids_filenames = [];
// Loop thru JS object to populate new array so it can be subsequently sorted
$.each($obj_win_top_get_files, function(key, value) {
  // Populate array
  arr_ids_filenames.push({
    id: key,
    name: value
  });
});

// Sort the array by file name (by using a comparator):
arr_ids_filenames.sort(function(a, b) {
  return a.name < b.name;
});

console.log('sort: ' + JSON.stringify(arr_ids_filenames));

// And finally insert them in the DOM:
var $the_ul = $('ul');
$.each(arr_ids_filenames, function(idx, file) {
  if (file.name) {
    var key = file.id;
    var value = file.name;
    console.log('here');
    $the_ul.append('<li id="chk_file_' + key + '">' + value + '</li>');
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<ul></ul>

5 Comments

Thanks so much nem for the detailed answer. I am very close now. The array is sorting by ID and I need to sort by file name. Also the value in the <li></li> tags is "undefined". Any recommendations?
No problem, glad to help! I updated the code to sort by file names instead of ids and the running sample code I posted here shows how to put the right values in the li tags.
Nem, thanks again and this is very close. The only thing that is not working is retaining the file ids. The 'key' var is undefined. $.each(arr_ids_filenames, function(idx, file) { if (file.name) { var key = file.key; var value = file.name; console.log('here'); $the_ul.append('<li id="chk_file_' + key + '">' + value + '</li>'); } });
I just noticed a small error. In the last .each, I was accessing file.key and should have been accessing file.id. It's fixed now, check out the updated answer/snippet, hope it helps!
Yes, that's fixes your solution and I confirmed it works. Thanks again for your time!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.