views:

76

answers:

3

I have some data in a table that looks like this:

Adam    $50
Evan    $20
Frank   $80
Harold  $90
Jeff    $20
Linus   $10
Sam     $87
Zach    $100

Results are sorted by name alphabetically and can have any amount of $ beside them.

I need to make the top five amounts bolded and red as well as append a #1, #2, #3 etc beside the first five names.

Can jquery do this without sorting by the $ column?

+4  A: 
$(function ()
{
    var i = 5,
        values = $('#myTable').find('td:odd').map(function (i, elt)
                 {
                     var $elt = $(elt);
                     return {$td: $elt,
                             value: parseFloat($elt.text().slice(1))};
                 }).get().sort(function (a,b)
                 {
                    return b.value-a.value;
                 });

    while(i--)
    {
        values[i].$td.css({fontWeight: 'bold', color: '#F00'})
                     .prev().append(' #' + (i+1));
    }
});

Functioning demo: http://jsbin.com/iseme3/7

Feel free to ask about anything in there you don't understand.

Matt Ball
You crazy. +1 for crazy.
Stefan Kendall
@Stefan: what, all the OCD edits? :P
Matt Ball
Haha, no. It's just a creative solution to a problem that I might have written much more code to solve.
Stefan Kendall
I wonder if that's because of the functional programming approach I took. At any rate, it was basically just the first way that came to mind. I could probably make even more terse.
Matt Ball
Absolutely beautiful! If instead of all the mad scientist babble around functional programming they would focus on elegant problem => solution as that, I'm sure it would be way more popular.
F.Aquino
I didn't even realize that map was a jQuery function. I guess I've never needed it, but then again, I probably did. I'm a huge fan of reducing LOC through functional programming, and this is a prime example of such.
Stefan Kendall
+1  A: 

Not directly, this is too concrete to be handled by an abstract framework layer. However, you can accomplish this fairly easily. For a table like :

<table id="table_id">
   <tr><th class="rank"></th><th class="name">Name</th><th class="amount">Amount</th></tr>

   <tr><td></td><td>Adam</td><td>$50</td></tr>
   <tr><td></td><td>Evan</td><td>$20</td></tr>
   <tr><td></td><td>Frank</td><td>$80</td></tr>
   <tr><td></td><td>Harold</td><td>$90</td></tr>
   <tr><td></td><td>Jeff</td><td>$20</td></tr>
   <tr><td></td><td>Linus</td><td>$10</td></tr>
   <tr><td></td><td>Sam</td><td>$87</td></tr>
   <tr><td></td><td>Zach</td><td>$100</td></tr>
</table>

You can do :

var top = 5;  // apply only transformation to the top n rows
var rankCol, nameCol, amountCol;

$('#table_id').find('tr')
   // get first row and find where are the name and amount columns...
   .slice(0, 1).each(function(i,row) {
      rankCol = $(row).find('.rank').index();
      nameCol = $(row).find('.name').index();
      amountCol = $(row).find('.amount').index();
   }).end()
   // remove first row
   .slice(1)
   // sort returned set by 'amount' in ascending order
   .sort(function(a,b) {
      return parseFloat($(a).find('td:eq('+amountCol+')').text().substr(1)) - parseFloat($(b).find('td:eq('+amountCol+')').text().substr(1));
   })
   // get the last 'top' rows
   .slice(-top)
   // apply bold + red to column 'amount'
   .find('td:eq('+amountCol+')').css({'font-weight':'bold', 'color':'red'}).end()
   // prepend #n to column 'rank'
   .find('td:eq('+rankCol+')').each(function(i,col) {
      $(col).text('#'+(top-i)+'. ');
   })
;

The code was organized for readability, but also so you can see how JQuery can apply a reusable pattern to chain up the calls on your results. If your table layout is different, for example you have 5 columns and "amount" is in the 5th, then the code will still work without modification, all you need is to set some class name to your "name" and "amount" columns and look for that class in the first row.

** EDIT ** I also added a reserved column to put the rank's text. This render nameCol obsolete, but I kept it in the code anyway. Having such a reserved column aligns everything beautifully. :) The number of top rows are now variable based. ... I could still play with the code to make it even more flexible / reusable, but I think you get the point.

** NOTE ** you could replace the css() call with addClass() instead that will define bold + red. You could even replace the prepend(string) call with a prepend(element) instead so you can actually style the "#n" before the name.

Yanick Rochon
A: 

Here's another approach.

var arr = $('#mytable').find('td:last-child')
  .map( function(){
    var cleanval = parseInt( $(this).text().replace('$',''),10 );
    $(this).addClass('s_'+cleanval);
    return cleanval;
  })
  .get()
  .sort( function(a,b){ return (b-a); } )
  .slice(4);

for( var i=0,j=arr.length;i<j;i++ ) {
    $('td.s_'+arr[i]).addClass('colorize');
}

​ Here's a working fiddle.

Ken Redler