多于一个“列”(键)的多维数组的排序数组,带有指定的排序选项

问题描述 投票:6回答:5

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

)

UPDATE:我认为理想情况下,一种解决方案将导致动态创建

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

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

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

array_multisort()

导致出现

call_user_func_array( 'array_multisort', $functionArgs);
php arrays sorting multidimensional-array
5个回答
4
投票

在PHP 5.3中,使用Warning: Parameter 2 to array_multisort() expected to be a reference, value given in... 调用array_multisort()时,数组中的每个参数都必须是引用。

此函数对多维数组进行排序,并显示了一种构建可以正常工作的引用参数数组的方法。

call_user_func_array()

示例:

$ data是问题中给定的数组

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

3
投票

这应该适合您所描述的情况。

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

您可以像这样将其压缩为一行:

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


1
投票

您可能想尝试使用array_multisort()。您所要做的就是创建一个告诉分类器如何对其进行分类的函数。该文档具有有关如何执行此操作的更多信息。


1
投票

这是我最终决定能够对多维数组进行排序的内容。上面的两个答案都不错,但我也在寻找一些灵活的方法。

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

[您从我的array_multisort()中的usort注释中可以看到,它是根据PHP手册中目前似乎不存在的注释改编而成的,但是我相信@link是原始版本的新版本。评论。我还没有使用新建议进行探索。

_usortByMultipleKeys()

0
投票

[我已经看到了许多在http://www.php.net/manual/en/function.usort.php#104398内使用/** * Sort the resultSet. * * Usage: $sortOptions = array( * 'section', // Defaults to SORT_ASC * 'row' => SORT_DESC, * 'retail_price' => SORT_ASC); * $results->sortResults($sortOptions); * * @param array $sortOptions An array of sorting instructions */ public function sortResults(array $sortOptions) { usort($this->_results, $this->_usortByMultipleKeys($sortOptions)); } /** * Used by sortResults() * * @link http://www.php.net/manual/en/function.usort.php#103722 */ protected function _usortByMultipleKeys($key, $direction=SORT_ASC) { $sortFlags = array(SORT_ASC, SORT_DESC); if (!in_array($direction, $sortFlags)) { throw new InvalidArgumentException('Sort flag only accepts SORT_ASC or SORT_DESC'); } return function($a, $b) use ($key, $direction, $sortFlags) { if (!is_array($key)) { //just one key and sort direction if (!isset($a->$key) || !isset($b->$key)) { throw new Exception('Attempting to sort on non-existent keys'); } if ($a->$key == $b->$key) { return 0; } return ($direction==SORT_ASC xor $a->$key < $b->$key) ? 1 : -1; } else { //using multiple keys for sort and sub-sort foreach ($key as $subKey => $subAsc) { //array can come as 'sort_key'=>SORT_ASC|SORT_DESC or just 'sort_key', so need to detect which if (!in_array($subAsc, $sortFlags)) { $subKey = $subAsc; $subAsc = $direction; } //just like above, except 'continue' in place of return 0 if (!isset($a->$subKey) || !isset($b->$subKey)) { throw new Exception('Attempting to sort on non-existent keys'); } if ($a->$subKey == $b->$subKey) { continue; } return ($subAsc==SORT_ASC xor $a->$subKey < $b->$subKey) ? 1 : -1; } return 0; } }; } 的Stack Overflow解决方案,但是从PHP5.6起,splat运算符可以取消嵌套排序功能并允许对其进行独占调用。

假设您的数据被声明为array_multisort()

call_user_function_array()

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

$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'], ]; 函数,而只需使用$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 ]; 。实际上,如果删除所有异常抛出逻辑,则该片段的大小可能约为其一半。

TLDR;该技术的优点在于,您需要做的就是在“ splat运算符”(sanitizeColumnReference())的帮助下,将平面的列和标志数组以及输入数组(可通过引用进行修改)传递给$column = array_column($input, array_shift($rule));并且所有工作都完成了。

array_multisort()

您将如何调用自定义/动态排序功能:

...

输出:(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 }

dynamicSort($array, $sortingRules);  // this modifies by reference like native sorting functions
© www.soinside.com 2019 - 2024. All rights reserved.