views:

1004

answers:

3

I'm looking to be able to sort an array of multi-dimensional arrays on more than one column. To further complicate it I'd like to be able to set specific sort options per key/column. I have what is similar the result of a DB query, but doesn't actually come from one, therefore the need to sort it in PHP rather than SQL.

Array
(
    [0] => Array
        (
            [first_name] => Homer
            [last_name] => Simpson
            [city] => Springfield
            [state] => Unknown
            [zip] => 66735
        )

    [1] => Array
        (
            [first_name] => Patty
            [last_name] => Bouvier
            [city] => Scottsdale
            [state] => Arizona
            [zip] => 85250
        )

    [2] => Array
        (
            [first_name] => Moe
            [last_name] => Szyslak
            [city] => Scottsdale
            [state] => Arizona
            [zip] => 85255
        )

    [3] => Array
        (
            [first_name] => Nick
            [last_name] => Riviera
            [city] => Scottsdale
            [state] => Arizona
            [zip] => 85255
        )

)

I would like to be able to sort it similar to what could be done with a DB query. Oh, and sometimes a column/key needs to be specified by number.

What I had in mind was something similar to this:

$sortOptions = array( array( 'city', SORT_ASC, SORT_STRING ),
                      array( 'zip', SORT_DESC, SORT_NUMERIC),
                      array( 2, SORT_ASC, SORT_STRING) // 2='last_name'
                    );
$sorter = new MultiSort($data, $sortOptions );
$sortedData = $sorter->getSortedArray() ;
print_r( $jmsSorted);

What I would like to end up with is this:

Array
(
    [0] => Array
        (
            [first_name] => Nick
            [last_name] => Riviera
            [city] => Scottsdale
            [state] => Arizona
            [zip] => 85255
        )

    [1] => Array
        (
            [first_name] => Moe
            [last_name] => Szyslak
            [city] => Scottsdale
            [state] => Arizona
            [zip] => 85255
        )

    [2] => Array
        (
            [first_name] => Patty
            [last_name] => Bouvier
            [city] => Scottsdale
            [state] => Arizona
            [zip] => 85250
        )

    [3] => Array
        (
            [first_name] => Homer
            [last_name] => Simpson
            [city] => Springfield
            [state] => Unknown
            [zip] => 66735
        )

)

UPDATE: I think that ideally, a solution would result in dynamically creating

array_multisort( $city, SORT_ASC, SORT_STRING, $zip, SORT_DESC, SORT_NUMERIC, $last_name, SORT_ASC, SORT_STRING, $inputArray);

The problem is that I don't want to have to "hard code" those key names in there. I tried creating a solution based upon Example #3 Sorting database results from the array_multisort() documentation that ended up using array_multisort() but I cannot seem to find a way to use my dynamically built argument list for array_multisort().

My attempt was to "chain" those arguments together into an array and then

call_user_func_array( 'array_multisort', $functionArgs);

That results in an

Warning: Parameter 2 to array_multisort() expected to be a reference, value given in...
+1  A: 

You might want to try using usort. All you have to do is make a functions that tell the sorter how to sort it. The docs have more info on how to do that.

jacobangel
+1  A: 

This should work for the situation you describe.

usort($arrayToSort, "sortCustom");

function sortCustom($a, $b)
{
    $cityComp = strcmp($a['city'],$b['city']);
    if($cityComp == 0)
    {
     //Cities are equal.  Compare zips.
     $zipComp = strcmp($a['zip'],$b['zip']);
     if($zipComp == 0)
     {
      //Zips are equal.  Compare last names.
      return strcmp($a['last_name'],$b['last_name']);
     }
     else
     {
      //Zips are not equal.  Return the difference.
      return $zipComp;
     }
    }
    else
    {
     //Cities are not equal.  Return the difference.
     return $cityComp;
    }
}

You could condense it into one line like so:

function sortCustom($a, $b)
{
    return ($cityComp = strcmp($a['city'],$b['city']) ? $cityComp : ($zipComp = strcmp($a['zip'],$b['zip']) ? $zipComp : strcmp($a['last_name'],$b['last_name'])));
}

As far as having a customizable sort function, you're reinventing the wheel. Take a look at the array_multisort() function.

Andrew
A: 

In PHP 5.3 every parameter in the array has to be a reference when calling array_multisort() with call_user_func_array().

This function sorts a multidimensional array and shows a way to build an array of referenced params that works correctly.

function msort()
{
  $params = func_get_args();
  $array = array_pop($params);

  if (!is_array($array))
    return false;

  $multisort_params = array();
  foreach ($params as $i => $param) 
  {
    if (is_string($param)) 
    {
      ${"param_$i"} = array();
      foreach ($array as $index => $row) 
      {
        ${"param_$i"}[$index] = $row[$param];
      }
    }
    else 
      ${"param_$i"} = $params[$i];

    $multisort_params[] = &${"param_$i"};
  }
  $multisort_params[] = &$array; 

  call_user_func_array("array_multisort", $multisort_params);

  return $array;
}

Example:

$data is the given array from the question

$sorted_data = msort('city', SORT_ASC, SORT_STRING, 'zip', SORT_DESC, SORT_NUMERIC, $data)
port-zero