Solved

sorting a multi dimensional array - not working right

Posted on 2008-06-11
5
219 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:
RQuadling earned 500 total points
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
Once I realized I was referencing constants, all worked great

Thanks bro!
0
 
LVL 40

Expert Comment

by:RQuadling
Comment Utility
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

Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

Join & Write a Comment

Suggested Solutions

Since upgrading to Office 2013 or higher installing the Smart Indenter addin will fail. This article will explain how to install it so it will work regardless of the Office version installed.
A short article about problems I had with the new location API and permissions in Marshmallow
Use Wufoo, an online form creation tool, to make powerful forms. Learn how to choose which pages of your form are visible to your users based on their inputs. The page rules feature provides you with an opportunity to create if:then statements for y…
Learn how to set-up custom confirmation messages to users who complete your Wufoo form. Include inputs from fields in your form, webpage redirects, and more with Wufoo’s confirmation options.

763 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

Need Help in Real-Time?

Connect with top rated Experts

6 Experts available now in Live!

Get 1:1 Help Now