Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

sorting a multi dimensional array - not working right

Posted on 2008-06-11
5
Medium Priority
?
229 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 3
  • 2
5 Comments
 
LVL 40

Accepted Solution

by:
Richard Quadling earned 2000 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

Build and deliver software with DevOps

A digital transformation requires faster time to market, shorter software development lifecycles, and the ability to adapt rapidly to changing customer demands. DevOps provides the solution.

Question has a verified solution.

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

The SignAloud Glove is capable of translating American Sign Language signs into text and audio.
If you are a mobile app developer and especially develop hybrid mobile apps then these 4 mistakes you must avoid for hybrid app development to be the more genuine app developer.
Wufoo.com provides powerful tools for surveying targeted groups, and utilizing data from completed surveys to find trends, discover areas of demand or customer expectation, and make business decisions on products or services.
Progress

715 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