Solved

sorting a multi dimensional array - not working right

Posted on 2008-06-11
5
224 Views
Last Modified: 2012-06-21
I have a multidimensional array - the format used for population during a while loop is the first code snippet below

I would like to be able to sort the first tier contents by 3rd tier value either ascending or descending.

I have tried both arraymultisort and usort to no avail

I want to be able to have what field (project_code, client_name, billed, etc) being sorted by to be dynamic, as well as what direction (ASC / DESC)

What am I doing wrong here?
// array format during initial population
$this->lienEffort[$rs->fields[0]]['project_code'] = $rs->fields[0];
$this->lienEffort[$rs->fields[0]]['project_name'] = $project_name;
$this->lienEffort[$rs->fields[0]]['client_name'] = $client_name;
$this->lienEffort[$rs->fields[0]]['billed'] += $billed;
$this->lienEffort[$rs->fields[0]]['paid'] += $paid;
$this->lienEffort[$rs->fields[0]]['owed'] += ($billed - $paid);
$this->lienEffort[$rs->fields[0]]['last_mod_date'] = $rs->fields[1];
$this->lienEffort[$rs->fields[0]]['state'] = $state;
$this->lienEffort[$rs->fields[0]]['hours'] = $rs->fields[2];
 
 
 
// call to sort function (found on php.net usort man page)
$sort_direction = ($_SESSION['sortorderlien'] == 'ASC') ? 'a' : 'd';
$order_arr = array($_SESSION['sortlien'],$sort_direction);
$this->lienEffort = arfsort($this->lienEffort, $order_arr);
 
 
 
// sort functions found on php.net
function arfsort( $a, $fl ){
  $GLOBALS['__ARFSORT_LIST__'] = $fl;
  usort( $a, 'arfsort_func' );
  return $a;
}
 
// extended to allow sort direction per field sorted against
function arfsort_func( $a, $b ){
  foreach( $GLOBALS['__ARFSORT_LIST__'] as $f ) {
    switch ($f[1]) { // switch on ascending or descending value
      case "d":
        $strc = strcmp( strtolower($b[$f[0]]), strtolower($a[$f[0]]) );
        if ( $strc != 0 ){
          return $strc;
        }
      break;
      default:
        $strc = strcmp( strtolower($a[$f[0]]), strtolower($b[$f[0]]) );
        if ( $strc != 0 ){
          return $strc;
        }
      break;
    }
  }
  return 0;
}

Open in new window

0
Comment
Question by:trickyidiot
  • 3
  • 2
5 Comments
 
LVL 40

Accepted Solution

by:
Richard Quadling earned 500 total points
ID: 21764348
The basic issue is that your data is organised in rows (like a DB).

PHP wants to sort based on columns.

So, your data is ...

$a_Data = array
 (
 row1(name, address, phone),
 row2(name, address, phone),
 );

PHP's array_multisort wants ...

$a_Data = array
 (
 names=array(row1,row2),
 addresses=array(row1, row2),
 phone=array(row1, row2)
 );

sort of thing.




I built a function specifically for this issue.

It works on the results of a DB request (where you have rows of data) and doesn't need you to convert the entire set to columns first.

http://rquadling.php1h.com/array_multisort_column.php

Sorry for the ads. The site is on a freebie and I've not got around to moving it.

I've also attached the code below.

You use it exactly the same way as you would array_multisort. You need to use my constants to allow you to sort of column numbers as well as column names.

There are examples on the site also.

The original work was carried out by Kes (http://www.php.net/manual/en/function.array-multisort.php#68452). I just expanded it a bit.
<?php
/*
(C) UK 2006 Richard Quadling
 
This work is licensed under the Creative Commons Attribution 2.5 License.
To view a copy of this license, visit http://creativecommons.org/licenses/by/2.5/ or send a letter to
    Creative Commons,
    543 Howard Street,
    5th Floor,
    San Francisco,
    California,
    94105, USA.
 
 
Version History
 
2007/03/30
NO actual change, but to make this function PHP4 compatibility you will need to remove the type hints in the function definitions.
e.g.
function array_multisort_column(&$a_data, $m_mixed1)
and
function array_multisort_column_cmp(&$a_left, &$a_right)
 
Ideally, you should add some type checking to emulate the type hints.
if (is_array($a_data)) 
and
if (is_array($a_left) && in_array($a_right)
 
sort of thing.
 
2006/09/20
Removed reliance on PHP's constants as using column numbers conflicted with these constants.
Use AMC_SORT_xxx rather than SORT_xxx.
 
2006/08/23
Added CC license
Added notice and error for parameters
 
2006/08/22
Fixed local defines to be well out of the way of the ones used by PHP.
 
2006/08/07
Created this code based upon work done by KES (http://www.php.net/manual/en/function.array-multisort.php#68452)
*/
 
// AMC defines for keeping association.
define ('AMC_LOSE_ASSOCIATION', 'AMC10001');
define ('AMC_KEEP_ASSOCIATION', 'AMC10002');
 
// AMC defines for the global array.
define ('AMC_SORT_ORDER', 'AMC10003');
define ('AMC_SORT_TYPE', 'AMC10004');
 
// AMC defines for the sort order.
define ('AMC_SORT_ASC', 'AMC10005');
define ('AMC_SORT_DESC', 'AMC10006');
 
// AMC defines for sorting type.
define ('AMC_SORT_REGULAR', 'AMC10007');
define ('AMC_SORT_NUMERIC', 'AMC10008');
define ('AMC_SORT_STRING', 'AMC10009');
define ('AMC_SORT_STRING_CASELESS', 'AMC10010');
 
/**
  * bool array_multisort_column ( array ar1 [, mixed arg [, mixed ... [, array ...]]] )
  **/
function array_multisort_column(array &$a_data, $m_mixed1)
    {
    // Get the parameters and the number of parameters.
    $a_Args = func_get_args();
    $i_Args = func_num_args();
 
    // Define a global empty array for the comparison function.
    $GLOBALS['a_AMC_ordering'] = array();
 
    // Get the list of columns.
    $a_Columns = array_keys(reset($a_data));
 
    // Assume association is NOT kept
    $b_KeepAssociation = False;
 
    // Process the parameter list, extracting columns and any applicable settings.
    for($i_Arg = 1 ; $i_Arg < $i_Args ; )
        {
        // Initially we only want to look at columns.
        if (in_array($a_Args[$i_Arg], $a_Columns))
            {
            // Track the column.
            $s_Column = $a_Args[$i_Arg];
 
            // Add the column with default settings to the global array.
            $GLOBALS['a_AMC_ordering'][$a_Args[$i_Arg]] = array
                (
                AMC_SORT_ORDER => AMC_SORT_ASC,
                AMC_SORT_TYPE => AMC_SORT_REGULAR,
                );
 
            // While there are more parameters to process is the next one a controller rather than a column.
            while
                (
 
                // There IS a next parameter.
                isset($a_Args[$i_Arg + 1]) && 
 
                // It is a controller.
                in_array
                    (
                    $a_Args[$i_Arg + 1], 
                    array
                        (
                        AMC_KEEP_ASSOCIATION,
                        AMC_LOSE_ASSOCIATION,
                        AMC_SORT_STRING_CASELESS,
                        AMC_SORT_ASC,
                        AMC_SORT_DESC,
                        AMC_SORT_NUMERIC,
                        AMC_SORT_REGULAR,
                        AMC_SORT_STRING,
                        ), 
                    True
                    )
 
                )
                {
                // Deal with column sorting order.
                if (
                    in_array
                        (
                        $a_Args[$i_Arg + 1], 
                        array
                            (
                            AMC_SORT_ASC, 
                            AMC_SORT_DESC
                            ), 
                        True
                        )
                    )
                    {
                    $GLOBALS['a_AMC_ordering'][$s_Column][AMC_SORT_ORDER] = $a_Args[$i_Arg + 1];
                    }
                // Deal with column sorting type.
                elseif (
                    in_array
                        (
                        $a_Args[$i_Arg + 1], 
                        array
                            (
                            AMC_SORT_REGULAR, 
                            AMC_SORT_NUMERIC, 
                            AMC_SORT_STRING, 
                            AMC_SORT_STRING_CASELESS
                            ), 
                        True
                        )
                    )
                    {
                    $GLOBALS['a_AMC_ordering'][$s_Column][AMC_SORT_TYPE] = $a_Args[$i_Arg + 1];
                    }
                // Deal with array association.
                else
                    {
                    $b_KeepAssociation = (AMC_KEEP_ASSOCIATION == $a_Args[$i_Arg + 1]);
                    }
                // Take the next argument out of the picture.
                ++$i_Arg;
                }
            }
        // Allow array association to be defined.
        elseif (
            in_array
                (
                $a_Args[$i_Arg], 
                array
                    (
                    AMC_KEEP_ASSOCIATION, 
                    AMC_LOSE_ASSOCIATION
                    ), 
                True
                )
            )
            {
            $b_KeepAssociation = (AMC_KEEP_ASSOCIATION == $a_Args[$i_Arg]);
            }
        // Ignore sort options as they are not in the right place to be understood.
        elseif (
            in_array
                (
                $a_Args[$i_Arg], 
                array
                    (
                    AMC_SORT_REGULAR, 
                    AMC_SORT_NUMERIC, 
                    AMC_SORT_STRING, 
                    AMC_SORT_STRING_CASELESS
                    ), 
                True
                )
            )
            {
            trigger_error("Parameter $i_Arg of '{$a_Args[$i_Arg]}' is not applicable and has been ignored.", E_USER_NOTICE);
            }
        // Whatever is left is an error.
        else
            {
            trigger_error("Requested column of '{$a_Args[$i_Arg]}' is not present in the supplied array.", E_USER_ERROR);
            }
        // Get the next argument.
        ++$i_Arg;
        }
 
    // Determine which usort mechanism (with or without associations).
    $s_Sorter = ($b_KeepAssociation ? 'uasort' : 'usort');
 
    // Sort the data and get the result.
    $b_Result = $s_Sorter($a_data, 'array_multisort_column_cmp');
 
    // Remove the temporary global array.
    unset($GLOBALS['a_AMC_ordering']);
 
    // Return the results
    return $b_Result;
    }
 
/**
  * int array_multisort_column_cmp(array a_left, array a_right)
  **/
function array_multisort_column_cmp(array &$a_left, array &$a_right)
    {
    // Assume that the entries are the same.
    $i_Result = 0;
 
    // Process each column defined in the global array.
    foreach($GLOBALS['a_AMC_ordering'] as $s_Column => $a_ColumnData)
        {
        // Handle the different sort types.
        switch ($a_ColumnData[AMC_SORT_TYPE])
            {
            // Numeric.
            case AMC_SORT_NUMERIC :
                $i_ColumnCompareResult = 
                    ((intval($a_left[$s_Column]) == intval($a_right[$s_Column])) 
                    ? 
                        0 
                    : 
                        ((intval($a_left[$s_Column]) < intval($a_right[$s_Column])) 
                        ? 
                            -1 
                        : 
                            1
                        )
                    );
                break;
            // Case sensitive strings.
            case AMC_SORT_STRING :
                $i_ColumnCompareResult = strcmp((string)$a_left[$s_Column], (string)$a_right[$s_Column]);
                break;
            // Case insensitive strings.
            case AMC_SORT_STRING_CASELESS :
                $i_ColumnCompareResult = strcasecmp((string)$a_left[$s_Column], (string)$a_right[$s_Column]);
                break;
            // Regular sorting.
            case AMC_SORT_REGULAR :
            default :
                $i_ColumnCompareResult = 
                    (($a_left[$s_Column] == $a_right[$s_Column]) 
                    ? 
                        0 
                    : 
                        (($a_left[$s_Column] < $a_right[$s_Column]) 
                        ? 
                            -1 
                        : 
                            1
                        )
                    );
                break;
            }
        // Is the column in the two arrays the same?
        if (0 == $i_ColumnCompareResult)
            {
            // Continue with the next column.
            continue;
            }
        // Are we sorting descending?
        $i_Result = $i_ColumnCompareResult * (($a_ColumnData[AMC_SORT_ORDER] == AMC_SORT_DESC) ? -1 : 1);
 
        // As there is a difference, we do not need to continue with the remaining columns.
        break;
        }
 
    // Return the result.
    return $i_Result;
    }
?>

Open in new window

0
 
LVL 6

Author Comment

by:trickyidiot
ID: 21768563
the issue here is that The array is populated by the results of multiple database queries.

Setting up the array in the way that you mentioned:
$a_Data = array
 (
 names=array(row1,row2),
 addresses=array(row1, row2),
 phone=array(row1, row2)
 );

removes the first level association and leaves room for a lot of data mistakes - there has to be a better way to do this with the array structure I have in place.
0
 
LVL 6

Author Comment

by:trickyidiot
ID: 21770123
I'm getting the following error with your code:

Fatal error: Requested column of ''billed'' is not present in the supplied array. in C:\Inetpub\wwwroot\devpat\db\includes\functions.php on line 1482

that's within function array_multisort_column

using this code:
$sort_direction = ($_SESSION['sortorderlien'] == 'ASC') ? 'AMC_SORT_ASC' : 'AMC_SORT_DESC';
      
$sort_type = (is_numeric($_SESSION['sortlien'])) ? 'AMC_SORT_NUMERIC' : 'AMC_SORT_STRING';

array_multisort_column($this->lienEffort, "'".$_SESSION['sortlien']."'", $sort_direction, $sort_type);


$this->lienEffort is the array above, using numeric indexes for the first level rather than project code
$_SESSION['sortlien'] is the column to sort by
0
 
LVL 6

Author Closing Comment

by:trickyidiot
ID: 31466360
Once I realized I was referencing constants, all worked great

Thanks bro!
0
 
LVL 40

Expert Comment

by:Richard Quadling
ID: 21773350
Ah. I see what you mean. I forgot to include an example.

For the PAQ (note the use of the constants).

Sorry for the lack of example.




<?php
require_once './array_multisort_column.txt';
require_once './array_multisort_column_example_data.txt';
 
// Sort the data Location Case insensitive and descending.
array_multisort_column($a_Data, 'Location', AMC_SORT_DESC, AMC_SORT_STRING_CASELESS);
echo '<pre>', var_export($a_Data, True), '</pre>';
?>

Open in new window

0

Featured Post

Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Real-time is more about the business, not the technology. In day-to-day life, to make real-time decisions like buying or investing, business needs the latest information(e.g. Gold Rate/Stock Rate). Unlike traditional days, you need not wait for a fe…
Introduction This article is intended for those who are new to PHP error handling (https://www.experts-exchange.com/articles/11769/And-by-the-way-I-am-New-to-PHP.html).  It addresses one of the most common problems that plague beginning PHP develop…
The viewer will learn how to count occurrences of each item in an array.
The viewer will learn how to create and use a small PHP class to apply a watermark to an image. This video shows the viewer the setup for the PHP watermark as well as important coding language. Continue to Part 2 to learn the core code used in creat…

856 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question