Solved

sorting a multi dimensional array - not working right

Posted on 2008-06-11
5
221 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
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:RQuadling
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

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

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

Suggested Solutions

Whether you've completed a degree in computer sciences or you're a self-taught programmer, writing your first lines of code in the real world is always a challenge. Here are some of the most common pitfalls for new programmers.
These days socially coordinated efforts have turned into a critical requirement for enterprises.
Learn how to match and substitute tagged data using PHP regular expressions. Demonstrated on Windows 7, but also applies to other operating systems. Demonstrated technique applies to PHP (all versions) and Firefox, but very similar techniques will w…
With the power of JIRA, there's an unlimited number of ways you can customize it, use it and benefit from it. With that in mind, there's bound to be things that I wasn't able to cover in this course. With this summary we'll look at some places to go…

911 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

21 Experts available now in Live!

Get 1:1 Help Now