【问题标题】:Sort multidimensional array on column values DESC, then by length of another column DESC按列值 DESC 对多维数组进行排序,然后按另一列 DESC 的长度
【发布时间】:2011-12-21 20:55:45
【问题描述】:

我需要对一些不是来自数据库但结构类似于 sql 结果集的数据进行排序。

在 MySQL 中,我会编写如下查询来按两列对数据进行排序:

SELECT product, qty FROM stock ORDER BY qty DESC, LENGTH(product) DESC 

但是,在这种情况下,我需要使用 php.ini 执行此排序逻辑。具体来说,行首先按qty降序排序,然后按name的长度降序排序。

未排序的输入:

[
    ['name' => 'foo bar', 'qty' => 6],
    ['name' => 'foo bar bar foo', 'qty' => 10],
    ['name' => 'b', 'qty' => 5],
    ['name' => 'foo', 'qty' => 10],
    ['name' => 'bar', 'qty' => 6],
    ['name' => 'foo bar bar bar foo', 'qty' => 6],
]

排序后,我需要使用name 值作为键和qty 值作为平面关联数组的值来重构数据 完成的数组看起来像这样:

期望的输出:

[
    'foo bar bar foo' => 10,
    'foo' => 10,
    'foo bar bar bar foo' => 6,
    'foo bar' => 6,
    'bar' => 6,
    'b' => 5
]

【问题讨论】:

  • 另外,你想用 PHP 做这件事有什么特别的原因吗?
  • @Ascherer:数据不存在于 MySQL 中,我想避免将其全部放在那里的传输时间。我的 SELECT 语句只是一个例子。
  • 啊,k。这是有道理的

标签: php sorting multidimensional-array


【解决方案1】:

查看这个问题的答案:PHP array multiple sort - by value then by key?,看来array_multisort 是要走的路。 (我不太确定 array_multisort 是如何工作的,我只是把它搞砸了,它似乎工作)。

试试这个:

$arr = array(
  'foo bar' => 6,
  'foo' => 10,
  'bar' => 6,
  'b' => 5,
  'foo bar bar bar foo' => 6,
  'foo bar bar foo' => 10
);

array_multisort(array_values($arr), SORT_DESC,
  array_map(create_function('$v', 'return strlen($v);'), array_keys($arr)),
  SORT_DESC, $arr);

演示:http://codepad.org/mAttNIV7

更新:添加 array_map 以使其在执行之前按字符串的长度排序:

$str1 > $str2 而不是strlen($str1) > strlen($str2)

更新 2:在 PHP >= 5.3 中,您可以将 create_function 替换为真正的匿名函数。

array_map(function($v){return strlen($v);}, array_keys($arr))

演示2:http://codepad.viper-7.com/6qrFwj

【讨论】:

  • hmm,是的,这种方式比我的原始数组效果更好,我最初避免了这种情况,因为我不认为它会按长度对字符串进行排序,但它看起来像是大声笑
  • @Ascherer:它最初是通过 $a > $b 对字符串进行排序,它不是按长度排序,只是碰巧按那个顺序。我添加了一个array_map 以使其按长度排序。
  • @Ascherer: usort 不能同时对键和值进行排序。
【解决方案2】:

看看php的usortuasort

你应该能够定义一个可以这样排序的函数

不确定它是否可以轻松地与当前数组一起工作,但这个数组会

$array = array(
 array('name' => 'foo bar bar foo', 'qty' => 10 ),
 array('name' => 'foo', 'qty' => 6),
 array('name' => 'foo bar bar foo', 'qty' => 6 ),
 array('name' => 'foo bar', 'qty' => 6 )
);

uasort($array, 'arraySort');

function arraySort($a, $b)
{
    if($a['qty'] > $b['qty'])
        return 1;
    elseif($a['qty'] < $b['qty'])
        return -1;
    else
        if(strlen($a['name']) >= strlen($b['name']))
            return 1;
        else
            return -1;
}

【讨论】:

    【解决方案3】:

    根据长度对键进行排序的一个限制是:相等长度的键不会重新排序。假设我们需要在descending order 中按长度对密钥进行排序。

    $arr = array(
        "foo 0" => "apple",
        "foo 1" => "ball",
        "foo 2 foo 0 foo 0" => "cat",
        "foo 2 foo 0 foo 1 foo 0" => "dog",
        "foo 2 foo 0 foo 1 foo 1" => "elephant",
        "foo 2 foo 1 foo 0" => "fish",
        "foo 2 foo 1 foo 1" => "giraffe"
    );
    
    debug($arr, "before sort");
    $arrBad = $arr;
    sortKeysDescBAD($arrBad);
    debug($arrBad, "after BAD sort");
    sortKeysDescGOOD($arr);
    debug($arr, "after GOOD sort 2");
    
    function sortKeysDescBAD(&$arrNew) {
        $arrKeysLength = array_map('strlen', array_keys($arrNew));
        array_multisort($arrKeysLength, SORT_DESC, $arrNew);
        //return max($arrKeysLength);
    }
    
    function sortKeysDescGOOD(&$arrNew) {
        uksort($arrNew, function($a, $b) {
            $lenA = strlen($a); $lenB = strlen($b);
            if($lenA == $lenB) {
                // If equal length, sort again by descending
                $arrOrig = array($a, $b);
                $arrSort = $arrOrig;
                rsort($arrSort);
                if($arrOrig[0] !== $arrSort[0]) return 1;
            } else {
                // If not equal length, simple
                return $lenB - $lenA;
            }
        });
    }
    
    function debug($arr, $title = "") {
        if($title !== "") echo "<br/><strong>{$title}</strong><br/>";
        echo "<pre>"; print_r($arr); echo "</pre><hr/>";
    }
    

    输出将是:

    before sort
    Array
    (
        [foo 0] => apple
        [foo 1] => ball
        [foo 2 foo 0 foo 0] => cat
        [foo 2 foo 0 foo 1 foo 0] => dog
        [foo 2 foo 0 foo 1 foo 1] => elephant
        [foo 2 foo 1 foo 0] => fish
        [foo 2 foo 1 foo 1] => giraffe
    )
    
    after BAD sort
    Array
    (
        [foo 2 foo 0 foo 1 foo 0] => dog
        [foo 2 foo 0 foo 1 foo 1] => elephant
        [foo 2 foo 0 foo 0] => cat
        [foo 2 foo 1 foo 0] => fish
        [foo 2 foo 1 foo 1] => giraffe
        [foo 0] => apple
        [foo 1] => ball
    )
    
    after GOOD sort
    Array
    (
        [foo 2 foo 0 foo 1 foo 1] => elephant
        [foo 2 foo 0 foo 1 foo 0] => dog
        [foo 2 foo 1 foo 1] => giraffe
        [foo 2 foo 1 foo 0] => fish
        [foo 2 foo 0 foo 0] => cat
        [foo 1] => ball
        [foo 0] => apple
    )
    

    注意elephantdog 例如(或其他)在两种排序方法中的顺序。第二种方法看起来更好。可能有更简单的方法来解决这个问题,但希望这对某人有所帮助......

    【讨论】:

      【解决方案4】:

      从类似于 sql 结果集的输入数组开始,您可以干净地使用 usort(),其中包含一个 3 路比较,然后是一个有条件的辅助 3 路比较。完成后,您可以将qty 数据列隔离为值,并且(假设所有name 值都是唯一的)使用name 列作为分配的键,使用array_column()。比较时,将$b数据写在运算符的左边,$a写在右边,实现降序排序。

      代码:(Demo)

      $array = [
          ['name' => 'foo bar bar foo', 'qty' => 6],
          ['name' => 'bah', 'qty' => 5],
          ['name' => 'foo foo bar foo', 'qty' => 10],
          ['name' => 'foo', 'qty' => 6],
          ['name' => 'foo bar', 'qty' => 6],
          ['name' => 'bar', 'qty' => 11],
      ];
      
      usort($array, function($a, $b) {
          return $b['qty'] <=> $a['qty'] ?: strlen($b['name']) <=> strlen($a['name']);
      });
      
      var_export(array_column($array, 'qty', 'name'));
      

      输出:

      array (
        'bar' => 11,
        'foo foo bar foo' => 10,
        'foo bar bar foo' => 6,
        'foo bar' => 6,
        'foo' => 6,
        'bah' => 5,
      )
      

      使用comparison1 ?: comparison2 的好处是comparison2 中的函数调用不会被执行,除非需要一个平局——这提高了效率。或者,使用array_multisort() 将无条件地对所有qty 值调用strlen()——即使排序不需要它们。

      使用单个 spaceship 运算符执行排序是完全有效的,但以下技术将在每次进行比较时进行两次函数调用。这样会比我上面的sn-p效率低,所以我不推荐下面的sn-p

      usort($array, function($a, $b) {
          return [$b['qty'], strlen($b['name']] <=> [$a['qty'], strlen($a['name'])];
      });
      

      附言当然这也可以用array_multisort() 来完成,我只是觉得语法不太简洁。

      array_multisort(
          array_column($array, 'qty'),
          SORT_DESC,
          array_map(
              function($row) {
                  return strlen($row['name']);
              },
              $array
          ),
          SORT_DESC,
          $array
      );
      
      var_export(array_column($array, 'qty', 'name'));
      

      【讨论】:

        【解决方案5】:

        在php中使用排序功能。确保使用 TRUE 作为第二个参数来保留密钥。

        【讨论】:

        • sort 没有“保留键”参数。如果你想这样做,你会使用asort
        猜你喜欢
        • 2017-01-12
        • 1970-01-01
        • 2023-04-04
        • 2011-11-22
        • 2021-05-03
        • 1970-01-01
        • 1970-01-01
        • 2016-03-26
        • 1970-01-01
        相关资源
        最近更新 更多