php字母排序 - sort ksort




我如何排序在PHP中的多維數組 (8)

這個問題在這裡已經有了答案:

我已將CSV數據加載到多維數組中。 這樣每個“行”是一條記錄,每個“列”包含相同類型的數據。 我正在使用下面的函數來加載我的CSV文件。

function f_parse_csv($file, $longest, $delimiter)
{
  $mdarray = array();
  $file    = fopen($file, "r");
  while ($line = fgetcsv($file, $longest, $delimiter))
  {
    array_push($mdarray, $line);
  }
  fclose($file);
  return $mdarray;
}

我需要能夠指定一個列進行排序,以便重新排列行。 其中一列包含格式為Ymd H:i:s日期信息,我希望能夠排序最近的日期為第一行。


介紹:針對PHP 5.3+的一個非常通用的解決方案

我想在這裡添加我自己的解決方案,因為它提供了其他答案不支持的功能。

具體而言,該解決方案的優點包括:

  1. 它是可重用的 :您將排序列指定為變量而不是對其進行硬編碼。
  2. 它很靈活 :您可以指定多個排序列(盡可能多) - 額外的列用作最初比較相等的項目之間的tiebreak。
  3. 這是可逆的 :您可以指定排序應該顛倒 - 針對每個列單獨排序。
  4. 它是可擴展的 :如果數據集包含無法以“啞”方式進行比較的列(例如日期字符串),則還可以指定如何將這些項目轉換為可以直接比較的值(例如DateTime實例)。
  5. 如果你想要話,它是關聯的 :這段代碼負責排序項目,但是選擇了實際的排序函數( usortuasort )。
  6. 最後,它不使用array_multisort :而array_multisort很方便,它依賴於在排序之前創建所有輸入數據的投影。 這會消耗時間和內存,並且如果數據集很大,可能會令人望而卻步。

代碼

function make_comparer() {
    // Normalize criteria up front so that the comparer finds everything tidy
    $criteria = func_get_args();
    foreach ($criteria as $index => $criterion) {
        $criteria[$index] = is_array($criterion)
            ? array_pad($criterion, 3, null)
            : array($criterion, SORT_ASC, null);
    }

    return function($first, $second) use (&$criteria) {
        foreach ($criteria as $criterion) {
            // How will we compare this round?
            list($column, $sortOrder, $projection) = $criterion;
            $sortOrder = $sortOrder === SORT_DESC ? -1 : 1;

            // If a projection was defined project the values now
            if ($projection) {
                $lhs = call_user_func($projection, $first[$column]);
                $rhs = call_user_func($projection, $second[$column]);
            }
            else {
                $lhs = $first[$column];
                $rhs = $second[$column];
            }

            // Do the actual comparison; do not return if equal
            if ($lhs < $rhs) {
                return -1 * $sortOrder;
            }
            else if ($lhs > $rhs) {
                return 1 * $sortOrder;
            }
        }

        return 0; // tiebreakers exhausted, so $first == $second
    };
}

如何使用

在本節中,我將提供對這個樣本數據集進行排序的鏈接:

$data = array(
    array('zz', 'name' => 'Jack', 'number' => 22, 'birthday' => '12/03/1980'),
    array('xx', 'name' => 'Adam', 'number' => 16, 'birthday' => '01/12/1979'),
    array('aa', 'name' => 'Paul', 'number' => 16, 'birthday' => '03/11/1987'),
    array('cc', 'name' => 'Helen', 'number' => 44, 'birthday' => '24/06/1967'),
);

基礎

函數make_comparer接受可變數量的參數,這些參數定義了所需的排序並返回一個函數,您應該使用它作為usortuasort的參數。

最簡單的用例是傳入您想要用來比較數據項的密鑰。 例如,要按name項目對$data進行排序,您可以執行此操作

usort($data, make_comparer('name'));

在行動中看到它

如果項目是數字索引數組,則該鍵也可以是數字。 對於問題中的例子,這將是

usort($data, make_comparer(0)); // 0 = first numerically indexed column

在行動中看到它

多個排序列

您可以通過將其他參數傳遞給make_comparer來指定多個排序列。 例如,要按“數字”排序,然後按零索引列排序:

usort($data, make_comparer('number', 0));

在行動中看到它

高級功能

如果您將排序列指定為數組而不是簡單字符串,則可以使用更高級的功能。 這個數組應該用數字索引,並且必須包含這些項目:

0 => the column name to sort on (mandatory)
1 => either SORT_ASC or SORT_DESC (optional)
2 => a projection function (optional)

讓我們看看我們如何使用這些功能。

反向排序

按名稱降序排序:

usort($data, make_comparer(['name', SORT_DESC]));

在行動中看到它

按數字降序排序,然後按名稱降序排序:

usort($data, make_comparer(['number', SORT_DESC], ['name', SORT_DESC]));

在行動中看到它

自定義投影

在某些情況下,您可能需要按列的值進行排序,該列的值不能很好地排序。 樣本數據集中的“生日”一欄符合以下描述:將生日作為字符串進行比較沒有意義(因為例如“01/01/1980”在“10/10/1970”之前)。 在這種情況下,我們要指定如何將實際數據投影直接與所需語義進行比較的表單。

可以將投影指定為任何類型的callable :字符串,數組或匿名函數。 假設投影接受一個參數並返回其投影形式。

應該注意的是,雖然預測與用於usort和family的自定義比較函數相似,但它們更簡單(您只需將一個值轉換為另一個值),並利用已經存入make_comparer的所有功能。

讓我們對沒有投影的示例數據集進行排序,看看會發生什麼:

usort($data, make_comparer('birthday'));

在行動中看到它

那不是理想的結果。 但是我們可以使用date_create作為投影:

usort($data, make_comparer(['birthday', SORT_ASC, 'date_create']));

在行動中看到它

這是我們想要的正確順序。

預測可以實現更多的事情。 例如,快速獲取不區分大小寫的排序方式是使用strtolower作為投影。

也就是說,我還應該提到,如果數據集很大,最好不要使用投影:在這種情況下,事先手動投影所有數據然後在不使用投影的情況下進行排序會快得多,儘管這樣做會交易增加內存使用量以提高分類速度。

最後,這是一個使用所有功能的示例:首先按數字降序排序,然後按生日昇序排序:

usort($data, make_comparer(
    ['number', SORT_DESC],
    ['birthday', SORT_ASC, 'date_create']
));

在行動中看到它


使用閉包進行多行排序

這是另一種使用uasort()和匿名回調函數(閉包)的方法。 我經常使用這個功能。 需要PHP 5.3 - 不再需要依賴關係!

/**
 * Sorting array of associative arrays - multiple row sorting using a closure.
 * See also: http://the-art-of-web.com/php/sortarray/
 *
 * @param array $data input-array
 * @param string|array $fields array-keys
 * @license Public Domain
 * @return array
 */
function sortArray( $data, $field ) {
    $field = (array) $field;
    uasort( $data, function($a, $b) use($field) {
        $retval = 0;
        foreach( $field as $fieldname ) {
            if( $retval == 0 ) $retval = strnatcmp( $a[$fieldname], $b[$fieldname] );
        }
        return $retval;
    } );
    return $data;
}

/* example */
$data = array(
    array( "firstname" => "Mary", "lastname" => "Johnson", "age" => 25 ),
    array( "firstname" => "Amanda", "lastname" => "Miller", "age" => 18 ),
    array( "firstname" => "James", "lastname" => "Brown", "age" => 31 ),
    array( "firstname" => "Patricia", "lastname" => "Williams", "age" => 7 ),
    array( "firstname" => "Michael", "lastname" => "Davis", "age" => 43 ),
    array( "firstname" => "Sarah", "lastname" => "Miller", "age" => 24 ),
    array( "firstname" => "Patrick", "lastname" => "Miller", "age" => 27 )
);

$data = sortArray( $data, 'age' );
$data = sortArray( $data, array( 'lastname', 'firstname' ) );

你可以使用array_multisort()

嘗試這樣的事情:

foreach ($mdarray as $key => $row) {
    // replace 0 with the field's index/key
    $dates[$key]  = $row[0];
}

array_multisort($dates, SORT_DESC, $mdarray);

對於PHP> = 5.5.0,只需提取列進行排序即可。 無需循環:

array_multisort(array_column($mdarray, 0), SORT_DESC, $mdarray);

在我能夠運行TableSorter類之前,我已經提出了一個基於提供的函數。

function sort2d_bycolumn($array, $column, $method, $has_header)
  {
  if ($has_header)  $header = array_shift($array);
  foreach ($array as $key => $row) {
    $narray[$key]  = $row[$column]; 
    }
  array_multisort($narray, $method, $array);
  if ($has_header) array_unshift($array, $header);
  return $array;
  }
  • $ array是您想要排序的MD Array。
  • $列是您希望排序的列。
  • $方法是您希望執行排序的方式,例如SORT_DESC
  • 如果第一行包含您不想排序的標題值,則$ has_header被設置為true。

我更喜歡使用array_multisort。 請參閱here的文檔。


我知道這個問題已經過了2年,但是這裡有另一個函數來排序一個二維數組。 它接受可變數量的參數,允許您傳入多個鍵(即列名)進行排序。 PHP 5.3需要。

function sort_multi_array ($array, $key)
{
  $keys = array();
  for ($i=1;$i<func_num_args();$i++) {
    $keys[$i-1] = func_get_arg($i);
  }

  // create a custom search function to pass to usort
  $func = function ($a, $b) use ($keys) {
    for ($i=0;$i<count($keys);$i++) {
      if ($a[$keys[$i]] != $b[$keys[$i]]) {
        return ($a[$keys[$i]] < $b[$keys[$i]]) ? -1 : 1;
      }
    }
    return 0;
  };

  usort($array, $func);

  return $array;
}

試試這裡: http://www.exorithm.com/algorithm/view/sort_multi_array : http://www.exorithm.com/algorithm/view/sort_multi_array


隨著usort 。 這是一個通用的解決方案,您可以使用不同的列:

class TableSorter {
  protected $column;
  function __construct($column) {
    $this->column = $column;
  }
  function sort($table) {
    usort($table, array($this, 'compare'));
    return $table;
  }
  function compare($a, $b) {
    if ($a[$this->column] == $b[$this->column]) {
      return 0;
    }
    return ($a[$this->column] < $b[$this->column]) ? -1 : 1;
  }
}

按第一列排序:

$sorter = new TableSorter(0); // sort by first column
$mdarray = $sorter->sort($mdarray);





multidimensional-array