views:

246

answers:

3

Using PHP, I scan a directory and list all of the .xml files. Each XML file contains both a "name" element and a "date" element. The "name" element for each XML file is listed as an option in the select list. This works perfectly fine, however, the "date" element in each XML file is different and contains a date format like this: mm/dd/yyyy. What I am trying to figure out how to do is sort the items according to the date, with the earliest date being first one the list and the most recent at the end.

Now say each of those items has a different value for the "date" element. I need to sort those items with the earliest date being first. I'm not sure how I can store the "date" element data somewhere so that it can be handled by JavaScript. I'm sorry if this is a very vague description, it's been baffling me for a while and it's a been confusing to try and explain.

UPDATED

So right now this is what I have working:

<select name="sel_id" onchange="this.form.submit()" size="20">
<option value="item1">Item 1</option>
<option value="item2">Item 2</option>
<option value="item3">Item 3</option>
<option value="item4">Item 4</option>
</select>

I guess one thing that would majorly help is knowing if there's a way to store the date somewhere in the tags besides the value attribute seeing how it's already being used. The date itself isn't a concern, I have that much figured it, it's just a matter of storing it somewhere so that it can be called client side.

+2  A: 

Updated

You need to:

  • use a custom attribute for the dates
  • use sort() function's custom compare function parameter
  • convert to array to make sorting possible
  • convert the dates for string comparison (mm/dd/yyyy -> yyyy-dd-mm)

See it in action

[Tested on: IE 5.5+, FF 2+, Chrome 4+, Safari 4+, Opera 9.6+]

HTML:

<select name="sel_id" id="sel_id" size="4">
  <option value="item2" date="02-01-2009">Item 2</option>
  <option value="item3" date="01-05-2010">Item 3</option>
  <option value="item1" date="10-06-2007">Item 1</option>
  <option value="item4" date="04-05-2011">Item 4</option>
</select>

Javascript:

// array functions are not working
// on nodeLists on IE, we need to
// to convert them to array
function toArray( obj ) {
  var i, arr = [];
  for ( i = obj.length; i--; ){
    arr[i] = obj[i];
  }
  return arr;
}

// custom compare function for sorting
// by the hidden date element
function sortByDate( el1, el2 ) {
  var a = convertToDate( el1.getAttribute("date") ),
      b = convertToDate( el2.getAttribute("date") );
  if ( a < b ) return -1;
  else if( a > b ) return 1;
  else return 0;
}

// convert date for string comparison
function convertToDate( date ) {
  date = date.split("-");
  return date[2] + "-" + date[0] + "-" + date[1];
}

// select the elements to be ordered
var itemsId = document.getElementById("sel_id"),
    items   = itemsId.getElementsByTagName("option"),
    len     = items.length;

// convert to array, to make sorting possible
items = toArray( items );

// do the item sorting by their date
items = items.sort( sortByDate );

// append them back to the parent in order
for ( var i = 0; i < len; i++ ) {
  itemsId.appendChild( items[i] );
}

​ ​

galambalazs
This isn't exactly a select element.
Nate Shoffner
it wasn't a about `<select>` in the question when i answered. I reorganize my answer. But be more specific next time..
galambalazs
I've updated. have fun! :) (the link is new too)
galambalazs
Thank you, works great. Sorry for the delayed replies. One last question. I easily turned this into a function so that I can call it from a link and it works fine, but I also tried implementing a function to sort the options back to alphabetical order. I've tried several different ways to do this and it works at first, sorting them by date works, then by name, but after that, neither sorting options work.
Nate Shoffner
I've made a new revision for your needs: http://jsbin.com/oraxi4/9/edit. Have Fun!
galambalazs
A: 

You can represent the dates as option values, or use a data attribute to store them with an option.

<option value="2010-07-05">..</option>

or

<option data-date="2010-07-05">..</option>

Assuming your select list looks like this:

<select id="myList">
    <option value="2010-07-01">Item 1</option>
    <option value="2010-06-21">Item 1</option>
    <option value="2010-08-22">Item 1</option>
    ..
</select>

Use the inbuilt Array.sort function with a custom comparator to sort the nodes, and once sorted re-insert them into the select list. See an example here.

/**
 * Sorts option elements by value attribute which holds a date
 *
 * @param {HTMLOptionElement} a first option
 * @param {HTMLOptionElement} b second option
 *
 * @returns {Integer}
 */
var sortByDate = function(a, b) {
    return new Date(a.value) - new Date(b.value);
}

var list = document.getElementById("myList");
var options = list.getElementsByTagName("option");
// NodeList that we get in previous step is an array-like
// object but not actually an array. Some call it a fuck-up,
// but regardless we need to convert it to an array.
var optionsArray = Array.prototype.slice.call(options);

// Now that we have an array, we can use the sort method (in-place sorting)
optionsArray.sort(sortByDate);

// re-insert the sorted nodes
for(var i = 0; i < optionsArray.length; i++) {
    list.appendChild(optionsArray[i]);
}
​
Anurag
Not gonna work. You can't create a date from a string like that, and you forgot to clear the options before putting the "sorted" ones in.
no
Well you can create a date from a string like `"yyyy-mm-dd"`. Works on Chrome and Firefox. If some browsers complain, manually compose the date or use a date library such as [Datejs](http://www.datejs.com/), but that is not the main point of the question. I purposely removed the code to clear the options array first as that is not needed. See [DOM specs for appendChild](http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-184E7107) - "Adds the node newChild **to the end of the list** of children of this node. If the newChild is already in the tree, **it is first removed**."
Anurag
You're right, I stand corrected on both counts. Although if the date strings are formatted like that, there's no reason to convert to a date... a string comparison should suffice. The thing you pointed out about appendChild is very interesting, thanks. (edit: on reviewing the OP, I see that his dates are formatted mm/dd/yyyy, so it looks like we have more work to do after all ;)
no
@no - mm/dd/yyyy is kinda problematic, but I think converting to date is going to helpful, as string comparisons are done lexicographically, so things like "10" < "2" evaluate to true. I realized the code could have been shorted after seeing the `appendChild` specs, and who doesn't like lesser code :)
Anurag
I just updated my question to hopefully ease some confusion.
Nate Shoffner
use the data attributes? `<option data-date="2010-07-05">..</option>`
Anurag
`"who doesn't like lesser code?"` - doesn't work in **IE**...
galambalazs
Anurag: if everything is zero-padded, the "10" < "2" thing won't matter, and there still won't be any need to convert to Date. Thinking about it more, though, it seems like a numeric comparison would be slightly more efficient than a string comparison. I'll update my suggestion in a bit.
no
galambalazs: how does it "not work?" Are the options duplicated, or not reordered at all, or does an error occur? I'd check myself but I don't have a copy of windows or IE on hand.
no
@galambalazs - what doesn't work on IE - date parsing, sorting, appending? I don't have access to IE so you'd have to be a bit more specific.
Anurag
wow, Negative votes for all answers here when all of them are clearly correct :) To the downvoter - the point of SO is to guide the OP to a solution, and not to give out fully working code that is well tested, maintainable, cross-browser, extensible, best-practice 1, best practice 2, best practice 3, etc. etc.
Anurag
A: 

Short and sweet. This version also accounts for dates being in mm-dd-yyyy format as per the OP's request.

<form>
  <select id="myList">
    <option value="07-01-2010">Item 2</option>
    <option value="09-21-2009">Item 1</option>
    <option value="08-22-2010">Item 3</option>
  </select>
</form>
<script>
  var list    = document.forms[0].myList,
      opts    = list.options,
      len     = opts.length,
      sorted  = [].slice.call(opts).sort(function(a,b){
        return fixDate(a.value) < fixDate(b.value) ? -1 : 1;
      });

  for (var i=0; i<len; i++) {
    list.appendChild(sorted[i]);
  }

  // convert m-d-y to y-m-d
  function fixDate (d) {
    var a=d.split('-');
    a.unshift(a.pop()); 
    return a.join('-');
  }

</script>
no