Smarty Forum Index Smarty
WARNING: All discussion is moving to https://reddit.com/r/smarty, please go there! This forum will be closing soon.

Sorting arrays inside templates
Goto page 1, 2  Next
 
This forum is locked: you cannot post, reply to, or edit topics.   This topic is locked: you cannot edit posts or make replies.    Smarty Forum Index -> General
View previous topic :: View next topic  
Author Message
cablehead
Smarty Rookie


Joined: 09 Jul 2003
Posts: 23

PostPosted: Mon Sep 15, 2003 2:09 am    Post subject: Sorting arrays inside templates Reply with quote

I did a few searches for this in the forum and everyone seems to agree that its bad idea.

Just a bit curious as to why - the order of say a list of users seems like a pretty arbitary thing. I've always thought it was a presentation issue?

I've been thinking about creating a modifier that would work something like:

<table>
<tr><td>Name</td><td>Address</td></tr>

{foreach from=$users|sortby:lastName item=user}
<tr>
<td>{$user.lastName}, {$user.firstName}</td>
<td>{$user.address}</td>
</tr>
{/foreach}

</table>

Is this a bad way to go?

Thanks !
Andy.
Back to top
View user's profile Send private message Send e-mail
cablehead
Smarty Rookie


Joined: 09 Jul 2003
Posts: 23

PostPosted: Tue Sep 16, 2003 7:02 am    Post subject: Reply with quote

Damn .. the sortby modifier seems to be called for each iteration of the foreach loop. Is this the expected behaviour?

Here's the code for the modifier so far. Hoping someone else is interested in this sort of thing as well.

Code:

#
# sorts an array of named arrays by the supplied fields
#   code by dholmes at jccc d0t net
#   taken from http://au.php.net/function.uasort
#
function array_sort_by_fields(&$data, $sortby){
    if(is_array($sortby)) {
        $sortby = join(',', $sortby);
    }
    uasort( $data,
         create_function( '$a, $b', '
            $skeys = split(\',\',\''.$sortby.'\');
            foreach($skeys as $key){
               if( ($c = strcasecmp($a[$key],$b[$key])) != 0 ){
                            return($c);
               }
            }
           return($c);  '));
}

#
# Modifier: sortby - allows arrays of named arrays to be sorted by a given field
#
function smarty_modifier_sortby($arrData,$sortfields) {
   return(array_sort_by_fields($arrData,$sortfields));
}

$smarty->register_modifier( "sortby", "smarty_modifier_sortby" );   


Any feedback is greatly appreciated !
Andy.
Back to top
View user's profile Send private message Send e-mail
messju
Administrator


Joined: 16 Apr 2003
Posts: 3336
Location: Oldenburg, Germany

PostPosted: Tue Sep 16, 2003 7:44 am    Post subject: Reply with quote

read http://smarty.php.net/manual/en/language.modifiers.php

it has to be
Code:
{foreach from=$users|@sortby:lastName item=user}...

(note the @) if you want to supply modifiers to arrays.

but then the modifier is still called twice (not once as would be sufficient). i don't consider this a severe bug, but i will see if this could be made leaner.

greetings
messju

(BTW to your overall question: sort order is on the borderline between display-logic and application-logic IMHO. i think it's fully valid to do this in the template like you do here.)
Back to top
View user's profile Send private message Send e-mail Visit poster's website
cablehead
Smarty Rookie


Joined: 09 Jul 2003
Posts: 23

PostPosted: Tue Sep 16, 2003 8:44 am    Post subject: Reply with quote

>> (note the @) if you want to supply modifiers to arrays.

Sorry messju. I should read the damn documentation Smile

I also made a mistake in the modifier. Should be:

Code:

function smarty_modifier_sortby($arrData, $sortfields) {
   array_sort_by_fields($arrData, $sortfields);
   return $arrData;
}


With messju's correction to the template code its working nicely if others were interested. I haven't tested yet but it should also allow:

Code:

{foreach from=$users|@sortby:"lastName,firstName" item=user}


Thanks to the clever function by dholmes at jccc d0t net .. who ever you may be Wink

>> i don't consider this a severe bug, but i will see if this could be made leaner.

Thank you for whatever you can do ...

Quote:

(BTW to your overall question: sort order is on the borderline between display-logic and application-logic IMHO. i think it's fully valid to do this in the template like you do here.)


I agree. It's a very grey area. Sort order could be a user perference stored in a database somewhere. Or dynamicly handled by client side javascript when the user clicks on a table header.

It is handy for the template designer to quickly take control of sorting without bugging the php coder in some cases though. Allowing easy to add modifiers like the above is a good compromise I think.

mmm smarty ... you are so sweet Smile
Back to top
View user's profile Send private message Send e-mail
messju
Administrator


Joined: 16 Apr 2003
Posts: 3336
Location: Oldenburg, Germany

PostPosted: Tue Sep 16, 2003 12:13 pm    Post subject: Reply with quote

nice sort-function but i see room for optimizations in it:
- with every call to array_sort_by_fields() there is a new function created. this is only necessary if the supplied $sortby was not already used before
- the split/foreach is done with every comparason. this loop can be unrolled when creating the function. so the split/foreach has only be done once per sort and not once per comparason.

(i hope phpBB doesn't not screw up my quoting:)
[php:1:f05af6d082]function array_sort_by_fields(&$data, $sortby)
{
static $sort_funcs = array();

if (empty($sort_funcs[$sortby])) {
$code = "\$c=0;";
foreach (split(',', $sortby) as $key) {
$code .= "if ( (\$c = strcasecmp(\$a['$key'],\$b['$key'])) != 0 ) return \$c;\n";
}
$code .= 'return $c;';
$sort_func = $sort_funcs[$sortby] = create_function('$a, $b', $code);
} else {
$sort_func = $sort_funcs[$sortby];
}
uasort($data, $sort_func);
}
[/php:1:f05af6d082]

with 1000 times sorting and 8-element array by 1 or 2 keys this one is about three times faster on my test-machine. of course the performance gain is not that big by only one or two calls per page.

greetings
messju

[EDIT: removed one redundant "$sort_func = $sort_funcs[$sortby];" ]


Last edited by messju on Fri Sep 19, 2003 7:48 am; edited 1 time in total
Back to top
View user's profile Send private message Send e-mail Visit poster's website
cablehead
Smarty Rookie


Joined: 09 Jul 2003
Posts: 23

PostPosted: Tue Sep 16, 2003 12:40 pm    Post subject: Reply with quote

cheers messju.

Unrolling the split and loop is a cool idea. For large arrays that would have been getting called a lot of times.

Do you mind if I post it back to http://au.php.net/function.uasort ? Its been a year since the original author posted it there, but there might be a slim chance of them getting the update.
Back to top
View user's profile Send private message Send e-mail
messju
Administrator


Joined: 16 Apr 2003
Posts: 3336
Location: Oldenburg, Germany

PostPosted: Tue Sep 16, 2003 12:55 pm    Post subject: Reply with quote

cablehead wrote:
Do you mind if I post it back to http://au.php.net/function.uasort ?


feel free Smile
Back to top
View user's profile Send private message Send e-mail Visit poster's website
pscsuk
Smarty n00b


Joined: 19 Aug 2005
Posts: 2

PostPosted: Fri Aug 19, 2005 9:39 am    Post subject: Reply with quote

I know this is a bit 'late', but here's my modification of this. Now, you can specify

Code:
{foreach from=$users|sortby"-name, #age" item=user}

The '-' lets you sort in reverse order, and the # lets you sort numerically rather than as a string (you can have '-#age' as well to sort numerically in reverse order)

Code:
<?php
#
# sorts an array of named arrays by the supplied fields
#   code by dholmes at jccc d0t net
#   taken from http://au.php.net/function.uasort
# modified by cablehead, messju and pscs at http://www.phpinsider.com/smarty-forum

function array_sort_by_fields(&$data, $sortby){
      static $sort_funcs = array();
     
    if (empty($sort_funcs[$sortby]))
    {
        $code = "\$c=0;";
        foreach (split(',', $sortby) as $key)
        {
           $d = '1';
              if (substr($key, 0, 1) == '-')
              {
                 $d = '-1';
                 $key = substr($key, 1);
              }
              if (substr($key, 0, 1) == '#')
              {
                 $key = substr($key, 1);
               $code .= "if ( ( \$c = (\$a['$key'] - \$b['$key'])) != 0 ) return $d * \$c;\n";
              }
              else
              {
               $code .= "if ( (\$c = strcasecmp(\$a['$key'],\$b['$key'])) != 0 ) return $d * \$c;\n";
            }
        }
        $code .= 'return $c;';
        $sort_func = $sort_funcs[$sortby] = create_function('$a, $b', $code);
    }
    else
    {
        $sort_func = $sort_funcs[$sortby];
    }   
    uasort($data, $sort_func);   
}

#
# Modifier: sortby - allows arrays of named arrays to be sorted by a given field
#
function smarty_modifier_sortby($arrData,$sortfields) {
   array_sort_by_fields($arrData,$sortfields);
   return $arrData;
}

$smarty->register_modifier( "sortby", "smarty_modifier_sortby" );   
?>

Have fun.
Back to top
View user's profile Send private message
pscsuk
Smarty n00b


Joined: 19 Aug 2005
Posts: 2

PostPosted: Fri Aug 19, 2005 9:51 am    Post subject: Reply with quote

Slight modification - change
Code:
$code .= "if ( ( \$c = (\$a['$key'] - \$b['$key'])) != 0 ) return $d * \$c;\n";

to
Code:
$code .= "if ( \$a['$key'] > \$b['$key']) return $d * 1;\n";
$code .= "if ( \$a['$key'] < \$b['$key']) return $d * -1;\n";


The original version didn't work well if the numbers were floating point and within '1' of each other - they were treated as equal. The new version works whatever.
Back to top
View user's profile Send private message
Alpha_Numeric
Smarty Rookie


Joined: 18 Aug 2008
Posts: 6

PostPosted: Mon Sep 08, 2008 12:34 am    Post subject: Reply with quote

Hi

I am trying to use this function on a non associative array contain associative arrays, e.g.,

Code:
0 => Array (12)
  ticketsid => "1301231460"
  eventid => "715574"
  event => "25th Annual Putnam County Spelling Bee"
  section => "BALC"
  row => "D"
  quantity => 4
  split => Array (2)
    0 => "4"
    1 => "2"
  saleprice => 125
  listprice => 90
  sellingbrokerid => 1031
  non_real_time => false
  notes => "BALCONY 4TH ROW SEATING"


I just get errors and am otherwise lost. I would like to figure out how to sort based both on strings and numeric values. e.g., sort on 'event' as well as 'quantity'.

If it is something that I am doing in the install of the files I will feel much better than if the function just doesn't like data in this fashion.

Any help, greatly appreciated.
Back to top
View user's profile Send private message
Celeb
Administrator


Joined: 17 Apr 2007
Posts: 1025
Location: Vienna

PostPosted: Mon Sep 08, 2008 8:32 am    Post subject: Reply with quote

This should work for your array:
Code:
{foreach from=$foo|@sortby:"event,#quantity"}

_________________
Darn computers always do what I tell them to instead of what I want them to do.
Back to top
View user's profile Send private message
blackhawkmx
Smarty Regular


Joined: 20 May 2005
Posts: 76

PostPosted: Mon Sep 29, 2008 6:37 pm    Post subject: Reply with quote

for the function above that labeled....

Code:

function array_sort_by_fields


what is the name of that .php page? and is it suppose to be placed in the smarty pluggins directory?

thanks!
bh
_________________
Bring it back to basics. You and I can make it right!
Back to top
View user's profile Send private message
Celeb
Administrator


Joined: 17 Apr 2007
Posts: 1025
Location: Vienna

PostPosted: Mon Sep 29, 2008 11:05 pm    Post subject: Reply with quote

It should be in a file named modifier.sortby.php in your plugins directory.
_________________
Darn computers always do what I tell them to instead of what I want them to do.


Last edited by Celeb on Fri Oct 17, 2008 7:25 am; edited 1 time in total
Back to top
View user's profile Send private message
Alpha_Numeric
Smarty Rookie


Joined: 18 Aug 2008
Posts: 6

PostPosted: Thu Oct 16, 2008 3:50 pm    Post subject: Reply with quote

It just doesn't work for me at all. I have placed this code, verbatim in a file called modifier.sortby.php, w/ the same permissions and ownership as all the other similar files in the ..libs/plugins directory:

Code:
<?php
#
# sorts an array of named arrays by the supplied fields
#   code by dholmes at jccc d0t net
#   taken from http://au.php.net/function.uasort
# modified by cablehead, messju and pscs at http://www.phpinsider.com/smarty-forum

function array_sort_by_fields(&$data, $sortby){
      static $sort_funcs = array();
     
    if (empty($sort_funcs[$sortby]))
    {
        $code = "\$c=0;";
        foreach (split(',', $sortby) as $key)
        {
           $d = '1';
              if (substr($key, 0, 1) == '-')
              {
                 $d = '-1';
                 $key = substr($key, 1);
              }
              if (substr($key, 0, 1) == '#')
              {
                 $key = substr($key, 1);
               $code .= "if ( \$a['$key'] > \$b['$key']) return $d * 1;\n";
$code .= "if ( \$a['$key'] < \$b['$key']) return $d * -1;\n";
              }
              else
              {
               $code .= "if ( (\$c = strcasecmp(\$a['$key'],\$b['$key'])) != 0 ) return $d * \$c;\n";
            }
        }
        $code .= 'return $c;';
        $sort_func = $sort_funcs[$sortby] = create_function('$a, $b', $code);
    }
    else
    {
        $sort_func = $sort_funcs[$sortby];
    }   
    uasort($data, $sort_func);   
}

#
# Modifier: sortby - allows arrays of named arrays to be sorted by a given field
#
function smarty_modifier_sortby($arrData,$sortfields) {
   array_sort_by_fields($arrData,$sortfields);
   return $arrData;
}

$smarty->register_modifier( "sortby", "smarty_modifier_sortby" );   
?>


The dump in Smarty debug tells me the array of arrays assigned to the Smarty variable $Tickets has these values:

Code:
    Array (7)
0 => Array (13)
  ticketsid => "1309823576"
  eventid => "728896"
  event => "Nine Inch Nails"
  section => "103"
  row => "E"
  quantity => 2
  rawsplit => "2"
  split => Array (1)
    0 => "2"
  saleprice => 170
  listprice => 124.95
  sellingbrokerid => 1022
  non_real_time => false
  notes => "AISLE SEATS--CENTER-STAGE VIEW!"
1 => Array (13)
  ticketsid => "1309823575"
  eventid => "728896"
  event => "Nine Inch Nails"
  section => "103"
  row => "G"
  quantity => 4
  rawsplit => "4,2"
  split => Array (2)
    0 => "4"
    1 => "2"
  saleprice => 160
  listprice => 119
  sellingbrokerid => 1022
  non_real_time => false
  notes => "CENTER-STAGE VIEW!"
2 => Array (13)
  ticketsid => "1309873207"
  eventid => "728896"
  event => "Nine Inch Nails"
  section => "107"
  row => "A"
  quantity => 2
  rawsplit => "2"
  split => Array (1)
    0 => "2"
  saleprice => 240
  listprice => 175
  sellingbrokerid => 982
  non_real_time => false
  notes => "E Tickets - Lower level"
3 => Array (13)
  ticketsid => "1309872734"
  eventid => "728896"
  event => "Nine Inch Nails"
  section => "120"
  row => "R"
  quantity => 8
  rawsplit => "8,6,4,2"
  split => Array (4)
    0 => "8"
    1 => "6"
    2 => "4"
    3 => "2"
  saleprice => 135
  listprice => 99
  sellingbrokerid => 1011
  non_real_time => false
  notes => "Tickets will ship on or before: 10/2..."
4 => Array (13)
  ticketsid => "1309865017"
  eventid => "728896"
  event => "Nine Inch Nails"
  section => "204"
  row => "N"
  quantity => 2
  rawsplit => "2"
  split => Array (1)
    0 => "2"
  saleprice => 120
  listprice => 89
  sellingbrokerid => 407
  non_real_time => false
  notes => "Upper Level Tickets."
5 => Array (13)
  ticketsid => "1309993523"
  eventid => "728896"
  event => "Nine Inch Nails"
  section => "Main Floor"
  row => "GA"
  quantity => 4
  rawsplit => "4,2"
  split => Array (2)
    0 => "4"
    1 => "2"
  saleprice => 165
  listprice => 120
  sellingbrokerid => 810
  non_real_time => false
  notes => "Main Floor right in front of the stage"
6 => Array (13)
  ticketsid => "1309993524"
  eventid => "728896"
  event => "Nine Inch Nails"
  section => "Main Floor"
  row => "GA"
  quantity => 4
  rawsplit => "4,2"
  split => Array (2)
    0 => "4"
    1 => "2"
  saleprice => 135
  listprice => 97.5
  sellingbrokerid => 810
  non_real_time => false
  notes => "Main Floor right in front of the stage"


...and this is the foreach loop in my .tpl file:

Code:
{foreach from=$Tickets|@sortby:"#saleprice" item="row" name="Tickets"}
                        {assign var=id value=$smarty.foreach.Tickets.index}
                        {assign var=ticket value=$Tickets.$id}
                        <tr{if $smarty.foreach.Tickets.index % 2 != 0} class="alt_color"{/if}>
                              {* ticketsid *}
                              <input name="ticketsid[]" type="hidden" value="{$ticket.ticketsid}"/>
                           <td>
                              <input name="index[]" type="checkbox" value="{$smarty.foreach.Tickets.index}"/>
                           </td>
                           <td>
                              <input name="section[]" type="hidden" value="{$ticket.section}"/>
                              {$ticket.section}
                           </td>
                           <td>
                              <input name="row[]" type="hidden" value="{$ticket.row}"/>
                              {$ticket.row}
                           </td>
                           <td class="price">
                              <input name="saleprice[]" type="hidden" value="{$ticket.saleprice}"/>
                              ${$ticket.saleprice}
                           </td>
                           <td>
                              <input name="split[]" type="hidden" value="{$ticket.rawsplit}"/>
                              <select name="qty[]">
                                 {foreach from=$ticket.split item="qty" name="qty"}
                                 <option value="{$qty}" {if $smarty.foreach.qty.last} selected{/if}>{$qty}</option>
                                 {/foreach}
                              </select>
                           </td>
                           <td class="last-child"><input name="ticket" type="image" value="submit" src="/images/button_buy_1.gif"/></td>
                        </tr>                        
                     {/foreach}


If I am doing something wrong, I cannot see what it is.

Please, any help greatly appreciated.
Back to top
View user's profile Send private message
Celeb
Administrator


Joined: 17 Apr 2007
Posts: 1025
Location: Vienna

PostPosted: Fri Oct 17, 2008 7:27 am    Post subject: Reply with quote

When you put the file in your plugins directory you shouldn't need the last line:
Code:
$smarty->register_modifier( "sortby", "smarty_modifier_sortby" );

How exactly does it not work? Do you get any errors or is the order wrong/not changed at all?
_________________
Darn computers always do what I tell them to instead of what I want them to do.
Back to top
View user's profile Send private message
Display posts from previous:   
This forum is locked: you cannot post, reply to, or edit topics.   This topic is locked: you cannot edit posts or make replies.    Smarty Forum Index -> General All times are GMT
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group
Protected by Anti-Spam ACP