使用指定的排序选项对多个“列”(键)上的多维数组排序

时间:2009-05-01 00:42:49

标签: php arrays sorting multidimensional-array

我希望能够在多个列上对多维数组进行排序。为了进一步复杂化,我希望能够为每个键/列设置特定的排序选项。我有类似于DB查询的结果,但实际上并不是来自一个,因此需要在PHP而不是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
        )

)

我希望能够对它进行排序,类似于使用数据库查询可以完成的操作。哦,有时候需要用数字来指定列/键。

我的想法与此类似:

$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);

我想最终得到的是:

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
        )

)

更新:我认为理想情况下,解决方案会导致动态创建

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

问题是我不想在那里“硬编码”那些关键名称。我尝试使用最终使用array_multisort()的{​​{3}}文档中的示例#3排序数据库结果创建解决方案但我似乎找不到使用动态构建的方法array_multisort()的参数列表。

我的尝试是将这些参数“链接”成一个数组然后

call_user_func_array( 'array_multisort', $functionArgs);

导致

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

5 个答案:

答案 0 :(得分:4)

在PHP 5.3中,使用array_multisort()调用call_user_func_array()时,数组中的每个参数都必须是引用。

此函数对多维数组进行排序,并显示构建正确工作的引用参数数组的方法。

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;
}

示例:

$ data是问题中给定的数组

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

答案 1 :(得分:3)

这适用于您描述的情况。

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;
    }
}

你可以将它压缩成一行,如下所示:

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'])));
}

就可定制的排序功能而言,你正在重新发明轮子。看一下array_multisort()函数。

答案 2 :(得分:1)

您可能想尝试使用usort。您所要做的就是创建一个函数,告诉分拣机如何对其进行排序。文档有关于如何做的更多信息。

答案 3 :(得分:1)

这是我最终决定能够对多维数组进行排序的内容。上面的两个答案都很好,但我也在寻找灵活的东西。

我绝对不认为有任何一个“正确”的答案,但这是符合我的需要并且灵活的。

正如您在@link的评论中所看到的那样,它是根据PHP手册中的评论改编而来的,目前似乎不存在,但我相信http://www.php.net/manual/en/function.usort.php#104398是原始评论的新版本。我没有探讨过使用这个新建议。

_usortByMultipleKeys()

答案 4 :(得分:0)

我已经看到许多在array_multisort()内使用call_user_function_array()的Stack Overflow解决方案,但是从PHP5.6起,splat运算符可以取消嵌套功能,并允许对其进行独占调用。 / p>

假设您的数据被声明为$array

$array = [
    ['first_name' => 'Homer', 'last_name' => 'Simpson', 'city' => 'Springfield', 'state' => 'Unknown', 'zip' => '66735'],
    ['first_name' => 'Patty', 'last_name' => 'Bouvier', 'city' => 'Scottsdale', 'state' => 'Arizona', 'zip' => '85250'],
    ['first_name' => 'Moe', 'last_name' => 'Szyslak', 'city' => 'Scottsdale', 'state' => 'Arizona', 'zip' => '85255'],
    ['first_name' => 'Nick', 'last_name' => 'Riviera', 'city' => 'Scottsdale', 'state' => 'Arizona', 'zip' => '85255'],
];

我的代码段将使用不确定数量的规则数组正确地对数据进行排序。它允许一个单独的字符串,该字符串指出应对ASC的哪一列进行排序,或者您可以显式声明排序标志。它的构建还允许在不事先知道确切的列名的情况下对列进行数字引用。

$sortingRules = [
    'city',                       // sort by city column ASC (could have also been: ['city'])
    ['zip', 'desc', 'numeric'],   // then sort by zip column DESC treating as numbers
    [1, 'desc'],                  // then sort by last_name column DESC
];

我已经创建了两个函数声明,但是如果您的项目从不希望处理传入的丢失/无效的列键,那么您可以消除sanitizeColumnReference()函数,而只需使用$column = array_column($input, array_shift($rule));。实际上,如果删除所有异常抛出逻辑,则该片段的大小大约可以是其一半。

TLDR;该技术的优点在于,您需要做的就是在“ splat运算符”({{的帮助下,将列和标志的平面数组,然后是输入数组(可通过引用进行修改))传递给array_multisort() 1}}),所有工作都完成了。

...

这是调用自定义/动态排序功能的方式:

function sanitizeColumnReference($row, $columnReference) {
    if (!isset($row[$columnReference]) && is_int($columnReference)) {
        $columnReference = array_keys($row)[$columnReference] ?? null;  // attempt to derive column name by position
        if ($columnReference === null) {
            throw new Exception('Failed to locate column by position using column reference: ' . $columnReference);
        }
    }
    return $columnReference;
}

function dynamicSort(&$input, $sortingRules) {
    if (!$input || !$sortingRules || !is_array($input) || !is_array($sortingRules)) {
        return;  // return silently
    }
    $firstRow = current($input);
    $sortingParams = [];
    foreach ($sortingRules as $rule) {
        $rule = (array)$rule; // permit the passing of a solitary string as a sorting rule
        $columnReference = sanitizeColumnReference($firstRow, array_shift($rule)); 
        $column = array_column($input, $columnReference);
        if (!$column) {
            throw new Exception('Failed to source sortable data from column reference: ' . $columnReference);
        }
        $sortingParams[] = $column;
        foreach ($rule as $flag) {
            $sortingParams[] = constant('SORT_' . strtoupper($flag));  // convert strings to usable CONSTANTs
        }
    }
    $sortingParams[] = &$input;
    // var_export($sortingParams);
    array_multisort(...$sortingParams); // unpack into native sorting function
}

输出:(Demo

dynamicSort($array, $sortingRules);  // this modifies by reference like native sorting functions