Sort Associative Array - Prototype Issue

Hello everyone,

I am building a customised javascript prototype to sort an associative array on the array key (in ascending order).

Basically, I am separating the array keys into a separate array and then using .sort() to sort the keys and then reassembling the original associative array elements according to the sorted keys array.

The sorting works ok except that when I run the test code below, the outputed sorted associative array has an extra element at the end of the array whose key is the name of the prototype function and the value for that element is the function code itself.

Obviously I am misunderstanding something about associative arrays or how javascript prototypes work.

Any help or solution on how to output a sorted associative array without the extra element will be much appreciated.

 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript">
 
/**********************************************
Prototype to sort an associative array
***********************************************/
 
Array.prototype.sortAssoc = function() {
 
//separate the keys into an array
var keys = new Array();
for(var i in this) {
       keys.push(i);
}
 
//now sort the keys
keys.sort();
 
//build the sorted array
var sortedArray = new Array();
for(var i=0; i < keys.length-1; i++) {
          sortedArray[keys[i]] = this[keys[i]];
}
 
return sortedArray;
 
}
 
//***** testing code **************
var myArray = [];
myArray['bbb'] = 'b_red';
myArray['aaa'] = 'a_green';
myArray['ccc'] = 'c_blue';
 
//sort the above array
var arraySorted = [];
arraySorted = myArray.sortAssoc();
 
//display the results
for(var i in arraySorted) {
         document.write(i+" = "+arraySorted[i]+'<br /><br />');
}
 
</script>
</head>
<body>
</body>
</html>

Hi felgall.

Thank you for your help.

I have made the change you suggested but unfortunately the ouput from the prototype function uis still not correct.

For debugging purposes I put an alert(this.length) at the top of the function and it pops up a value of 0 which explains why the document.write() at the end of the function now only outputs a single element in the sorted array - the key = the function name and the value is the function code itself.

Below is the test code as it stands at the moment.

What am I still misunderstnding? :confused:

btw - putting the prototype function code in a separate function results in the ouput of a correctly sorted array and so that is my plan B but I would prefer to have the sorting function as a prototype and so I am perservering with this.

 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript">
 
/**********************************************
Prototype to sort an associative array
***********************************************/
 
Array.prototype.sortAssoc = function() {
 
//alert(this.length);
 
//separate the keys into an array
var keys = new Array();

for( var i = this.length-1; i >= 0; i--) {
  keys.push(this[i]);
}
 
//now sort the keys
keys.sort();
 
//build the sorted array
var sortedArray = new Array();
for(var i=0; i < keys.length; i++) {
          sortedArray[keys[i]] = this[keys[i]];
}
 
return sortedArray;
 
}
 
//***** testing code **************
var myArray = [];
myArray['bbb'] = 'b_red';
myArray['aaa'] = 'a_green';
myArray['ccc'] = 'c_blue';
 
//sort the above array
var arraySorted = [];
arraySorted = myArray.sortAssoc();
 
//display the results
for(var i in arraySorted) {
         document.write(i+" = "+arraySorted[i]+'<br /><br />');
}
 
</script>
</head>
<body>
</body>
</html>

What about an if statement inside the first for-in then to skip the push() for those properties/methods you don’t want included.

It’s happening here:

for-in can pick-up other properties and while there may be some esoteric way to distinguish them, you could just use a stand alone function to avoid the problem.


function sortAssoc( arr ) 
{
 var keys = new Array();
 
 for( var i in arr ) 
  keys.push(i); 

 keys.sort();
 
 var sortedArray = new Array();
 
 for( var i = 0; i < keys.length; i++ )  
   sortedArray[keys[i]] = arr[keys[i]];
 
 return sortedArray;
}

I’m not sure what the point of this is.

ahhh, the penny has finally dropped :slight_smile:

As they say - if it looks like a duck and it quacks like a duck then it probably is a duck.

But unfortunately the same can’t be said in javascript :frowning:

If it looks like an associative array and it feels like an associative doesn’t mean it is an associative array :headbang:

After a bit of googling I see that javascript doesn’t have associative arrays but they are instead simply objects with properties as you all alluded to earlier.

Spmeone posted elsewhere that myArray[‘name’] = ‘fred’ is essentially the same as myArray.name = ‘fred’ and that explains why the prototype function is included in my “associative array” - because it is a property of the array I am trying to sort.

Anyway, my plan B (as was also suggested by Logic Ali) is now my plan A - put the prototype code in a separate function and everything works 100% without any problems.

Thank you to those that helped - it’s much appreciated as I learnt something new today :wink:

ok, I’ve added an IF to filter out the properties I don’t want and added some more alerts() to see what is going on at each stage.

There is something strange going on here and I am starting to think this has something to do with the way prototype objects/functions work which I don’t understand yet.

What is really strange to me is that in this snippet

//build the sorted array
var sortedArray = new Array();
for(var i=0; i < keys.length; i++) {
          //alert(keys[i]); //this alert outputs values - aaa, bbb, ccc
          sortedArray[keys[i]] = this[keys[i]];
}

the alert() outputs the expected c orrect values

but immediately below this loop this debugging snippet


//this for loop is for debugging only
for(var i in sortedArray) {
 alert(i);  ////this alert outputs values - aaa, bbb, ccc, sortAssoc
}

outputs as above but with the additional element with key = ‘sortAsscoc’.

It appears to me that no matter what code logic I use, something in javascript is appending prototype name and code to my array.

If you have a few minutes, I would appreciate it if you could have a look at the latest full test code below with the alerts() and their ouputs in comments next to them and see if you can makes sense of what is happening here.

Thank you again for your help.

 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript">
 
/**********************************************
Prototype to sort an associative array
***********************************************/
 
Array.prototype.sortAssoc = function() {
 
//separate the keys into an array
var keys = new Array();
for(var i in this) {

 //alert(i); //this alert outputs values - bbb, aaa, ccc, sortAssoc
   
if(i != 'sortAssoc') {

     //alert(i);  //this alert outputs values - bbb, aaa, ccc
    
 keys.push(i);
   }
}
 
//now sort the keys
keys.sort();
 
//build the sorted array
var sortedArray = new Array();
for(var i=0; i < keys.length; i++) {

          //alert(keys[i]); //this alert outputs values - aaa, bbb, ccc
          
sortedArray[keys[i]] = this[keys[i]];
}
 
//this for loop is for debugging only
for(var i in sortedArray) {

 alert(i);  ////this alert outputs values - aaa, bbb, ccc, sortAssoc

}
 
return sortedArray;
 
}
 
//***** testing code **************
var myArray = [];
myArray['bbb'] = 'b_red';
myArray['aaa'] = 'a_green';
myArray['ccc'] = 'c_blue';
 
//sort the above array
var arraySorted = [];
arraySorted = myArray.sortAssoc();
 
//display the results
for(var i in arraySorted) {
         document.write(i+" = "+arraySorted[i]+'<br /><br />');
}
 
</script>
</head>
<body>
</body>
</html>

Any day when you learn something is a good day.

Rereading my original post which I wrote early this morning I can see that I didn’t make it as clear as I should have that JavaScript doesn’t have associative arrays.

Hi Logic Ali,

Thank you for your reply.

Yes I thought it was happening where you said hence why I have a -1 further down in my code

 
for(var i=0; i < keys.length-1; i++) {

which made no difference to the output. That really through me into a spin :eek:

After my initial post I too put the code in a function instead similar to yours and it works fine but I was hoping to add the prototype to my prototype.js library :frowning:

What I am doing here is I have a client web page with a long list of subheadings, wrapped in <a> tags with content for each subheading. The subheadings are in the client’s requested order (not alphabetical) but the inline links at the top of the page to each subheading are in alphabetical order.

I am building an associative array (using javascript DOM methods) containing the subheading title (as key) and the href of each subheading’s <a> as the value. I will now use this associative array to dynamically generate the links at the top of the page to each subheading.

This way, when subheadings and their content are added or removed the links at the top will be automatically updated and kept in alphabetical order regardless of the order of the subheadings on the actual page.

JavaScript associative arrays are basically just a different notation for accessing objects and when you access an object using for-in ALL of its properties and methods (apart from the not enumerable ones) are processed. If you just want to process the properties then you need to use the length to control the loop iterations instead of a for-in since a for-in will copy the length as well as the other values that you want.

Instead of

for( var i in arr ) 
  keys.push(i); 

use

for( var i = arr.length-1; i >= 0; i--) 
  keys.push(arr[i]);