【问题标题】:Sort array of associative arrays on multiple columns using specified sorting rules使用指定的排序规则对多列上的关联数组进行排序
【发布时间】:2010-10-23 00:37:20
【问题描述】:

我希望能够对多个列上的关联数组进行排序。更复杂的是,我希望能够为每个键/列设置特定的排序选项。我有一组类似于 db 查询结果集的数据,但它实际上并不是来自一个,所以我需要用 PHP 而不是 SQL 对其进行排序。

[
    ['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'],
];

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

我的想法是这样的:

$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() 文档中的Example #3 Sorting database results 创建一个解决方案,该文档最终使用array_multisort(),但我似乎无法找到一种方法来为@ 使用我的动态构建的参数列表987654328@.

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

call_user_func_array( 'array_multisort', $functionArgs);

这会导致

警告:array_multisort() 的参数 2 应为引用,值在...中给出。

【问题讨论】:

    标签: php arrays sorting multidimensional-array


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      这应该适用于您描述的情况。

      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() 函数。

      【讨论】:

      • 我不同意重新发明轮子。据我所知,如果我想对数据进行排序,比如数据表,按一列排序,再按另一列排序,array_multisort() 不是正确的选择,因为它独立地对每个数组进行排序.我需要按特定顺序、特定方向对它们进行排序,我不知道第一手资料。 crazyj 的解决方案就是这种情况的解决方案。
      【解决方案3】:

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

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

      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)
      

      【讨论】:

        【解决方案4】:

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

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

        从我的@link_usortByMultipleKeys() 的评论中可以看出,它改编自 PHP 手册中目前似乎不存在的评论,但我相信 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;
                }
            };
        }
        

        【讨论】:

        • 我同意没有一个“正确”的答案,但是该死,这也非常接近我的需求,除了我需要对索引数组进行排序,而不是关联数组.
        • 我收到“警告:usort():数组已被用户比较函数修改”,使用此代码。我找到了以下解释,我会在修复时发表评论:stackoverflow.com/questions/3235387/…
        【解决方案5】:

        我在call_user_function_array() 中看到了许多使用array_multisort() 的 Stack Overflow 解决方案,但从 PHP5.6 开始,splat 运算符可以取消嵌套排序函数并允许以独占方式调用它。

        假设您的数据声明为$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'],
        ];
        

        我的 sn-p 将使用一组不确定的规则对您的数据进行正确排序。它允许使用一个单独的字符串来说明应该对哪一列进行 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));。事实上,如果你去掉所有的异常抛出逻辑,sn-p 的大小大约是它的一半。

        TLDR;这种技术的美妙之处在于,您需要做的就是在“splat 运算符”(...) 的帮助下,将一个平面列和标志数组,然后是输入数组(可通过引用修改)传递给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
        

        输出:(Demo)

        array (
          0 => 
          array (
            'first_name' => 'Moe',
            'last_name' => 'Szyslak',
            'city' => 'Scottsdale',
            'state' => 'Arizona',
            'zip' => '85255',
          ),
          1 => 
          array (
            'first_name' => 'Nick',
            'last_name' => 'Riviera',
            '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',
          ),
        )
        

        【讨论】:

          猜你喜欢
          • 2012-11-14
          • 1970-01-01
          • 2011-12-20
          • 2012-12-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多