natsort

(PHP 4, PHP 5)

natsort --  用“自然排序”算法对数组排序

说明

bool natsort ( array &array )

本函数实现了一个和人们通常对字母数字字符串进行排序的方法一样的排序算法并保持原有键/值的关联,这被称为“自然排序”。本算法和通常的计算机字符串排序算法(用于 sort())的区别见下面示例。

如果成功则返回 TRUE,失败则返回 FALSE

例子 1. natsort() 例子

<?php
$array1
= $array2 = array("img12.png", "img10.png", "img2.png", "img1.png");

sort($array1);
echo
"Standard sorting\n";
print_r($array1);

natsort($array2);
echo
"\nNatural order sorting\n";
print_r($array2);
?>

上例将输出:

Standard sorting
Array
(
    [0] => img1.png
    [1] => img10.png
    [2] => img12.png
    [3] => img2.png
)

Natural order sorting
Array
(
    [3] => img1.png
    [2] => img2.png
    [1] => img10.png
    [0] => img12.png
)

更多信息见 Martin Pool 的 Natural Order String Comparison 页面。

参见 natcasesort()strnatcmp()strnatcasecmp()


add a note add a note User Contributed Notes
@gmail bereikme
01-Sep-2006 11:06
Here's a handy function to sort an array on 1 or more columns using natural sort:
<?php
// Example: $records = columnSort($records, array('name', 'asc', 'addres', 'desc', 'city', 'asc'));

$globalMultisortVar = array();
function
columnSort($recs, $cols) {
   global
$globalMultisortVar;
  
$globalMultisortVar = $cols;
  
usort($recs, 'multiStrnatcmp');
   return(
$recs);
}

function
multiStrnatcmp($a, $b) {
   global
$globalMultisortVar;
  
$cols = $globalMultisortVar;
  
$i = 0;
  
$result = 0;
   while (
$result == 0 && $i < count($cols)) {
      
$result = ($cols[$i + 1] == 'desc' ? strnatcmp($b[$cols[$i]], $a[$cols[$i]]) : $result = strnatcmp($a[$cols[$i]], $b[$cols[$i]]));
      
$i+=2;
   }
   return
$result;
}

?>

Greetings,

  - John
nissar_pa at yahoo dot com
04-May-2006 10:50
Here is the program which will sort an array key in natural order and maintains key to data correlations

function natSortKey(&$arrIn)
{
   $key_array = array();
   $arrOut = array();
  
   foreach ( $arrIn as $key=>$value ) {
       $key_array[]=$key;
   }
  natsort( $key_array);
  foreach ( $key_array as $key=>$value ) {
     $arrOut[$value]=$arrIn[$value];
  }
  $arrIn=$arrOut;

}

Thanks,
Abdul Nissar
12-Mar-2006 11:44
The last comment should have been posted in doc about (r)sort( ). Indeed, and unfortunately, ORDER BY *does not* perform natural ordering. So, sometimes we *must* do a SQL request followed by natsort( ).
h3
23-Feb-2006 12:59
This function can be very usefull, but in some cases, like if you want to sort a MySQL query result, it's important to keep in mind that MySQL as built'in sorting functions which are way faster than resorting the result using a complex php algorythm, especially with large arrays.

ex; 'SELECT * FROM `table` ORDER BY columnName ASC, columnName2 DESC'
lil at thedreamersmaze dot spam-me-not dot org
01-Feb-2006 12:38
There's one little thing missing in this useful bit of code posted by mbirth at webwriters dot de:

<?php

function natsort2d(&$aryInput) {
 
$aryTemp = $aryOut = array();
  foreach (
$aryInput as $key=>$value) {
  
reset($value);
  
$aryTemp[$key]=current($value);
  }
 
natsort($aryTemp);
  foreach (
$aryTemp as $key=>$value) {
  
$aryOut[$key] = $aryInput[$key];
// --------^^^^ add this if you want your keys preserved!
 
}
 
$aryInput = $aryOut;
}

?>
natcasesort.too
05-Aug-2005 06:17
I got caught out through naive use of this feature - attempting to sort a list of image filenames from a digital camera, where the filenames are leading zero padded (e.g. DSCF0120.jpg) , will not sort correctly.
Maybe the example could be modified to exhibit this behaviour
(e.g. set array to -img0120.jpg','IMG0.png', 'img0012.png', 'img10.png', 'img2.png', 'img1.png', 'IMG3.png)
If the example hadn't used images I would have coded it correctly first time around!
mroach at mroach dot com
28-Jun-2005 12:08
Here's an expansion of the natsort2d function that mbirth wrote. This one allows you to specify the key for sorting.

<?php
function natsort2d( &$arrIn, $index = null )
{
  
  
$arrTemp = array();
  
$arrOut = array();
  
   foreach (
$arrIn as $key=>$value ) {
      
      
reset($value);
      
$arrTemp[$key] = is_null($index)
                           ?
current($value)
                           :
$value[$index];
   }
  
  
natsort($arrTemp);
  
   foreach (
$arrTemp as $key=>$value ) {
      
$arrOut[$key] = $arrIn[$key];
   }
  
  
$arrIn = $arrOut;
  
}

?>
phpnet at moritz-abraham dot de
03-Sep-2004 05:09
additional to the code posted by justin at redwiredesign dot com (which I found very usefull) here is a function that sorts complex arrays like this:
<?
$array
['test0'] = array('main' =>  'a', 'sub' => 'a');
$array['test2'] = array('main' =>  'a', 'sub' => 'b');
$array['test3'] = array('main' =>  'b', 'sub' => 'c');
$array['test1'] = array('main' =>  'a', 'sub' => 'c');
$array['test4'] = array('main' =>  'b', 'sub' => 'a');
$array['test5'] = array('main' =>  'b', 'sub' => 'b');
?>
or
<?
$array
[0] = array('main' =>  1, 'sub' => 1);
$array[2] = array('main' =>  1, 'sub' => 2);
$array[3] = array('main' =>  2, 'sub' => 3);
$array[1] = array('main' =>  1, 'sub' => 3);
$array[4] = array('main' =>  2, 'sub' => 1);
$array[5] = array('main' =>  2, 'sub' => 2);
?>
on one or more columns.

the code
<? $array = array_natsort_list($array,'main','sub'); ?>
will result in $array being sortet like this:
test0,test2,test1,test4,test5,test3
or
0,2,1,4,5,3.

you may even submit more values to the function as it uses a variable parameter list. the function starts sorting on the last and the goes on until the first sorting column is reached.

to me it was very usefull for sorting a menu having submenus and even sub-submenus.

i hope it might help you too.

here is the function:
<?
function array_natsort_list($array) {
  
// for all arguments without the first starting at end of list
  
for ($i=func_num_args();$i>1;$i--) {
      
// get column to sort by
      
$sort_by = func_get_arg($i-1);
      
// clear arrays
      
$new_array = array();
      
$temporary_array = array();
      
// walk through original array
      
foreach($array as $original_key => $original_value) {
          
// and save only values
          
$temporary_array[] = $original_value[$sort_by];
       }
      
// sort array on values
      
natsort($temporary_array);
      
// delete double values
      
$temporary_array = array_unique($temporary_array);
      
// walk through temporary array
      
foreach($temporary_array as $temporary_value) {
          
// walk through original array
          
foreach($array as $original_key => $original_value) {
              
// and search for entries having the right value
              
if($temporary_value == $original_value[$sort_by]) {
                  
// save in new array
                  
$new_array[$original_key] = $original_value;
               }
           }
       }
      
// update original array
      
$array = $new_array;
   }
   return
$array;
}
?>
xlab AT adaptiveNOSPAMarts DOT net
16-Jan-2004 04:51
Under limited testing, natsort() appears to work well for IP addresses. For my needs, it is far less code than the ip2long()/long2ip() conversion I was using before.
mbirth at webwriters dot de
10-Jan-2004 04:31
For those who want to natsort a 2d-array on the first element of each sub-array, the following few lines should do the job.

<?php

function natsort2d(&$aryInput) {
 
$aryTemp = $aryOut = array();
  foreach (
$aryInput as $key=>$value) {
  
reset($value);
  
$aryTemp[$key]=current($value);
  }
 
natsort($aryTemp);
  foreach (
$aryTemp as $key=>$value) {
  
$aryOut[] = $aryInput[$key];
  }
 
$aryInput = $aryOut;
}

?>
rasmus at flajm dot com
15-Dec-2003 01:30
To make a reverse function, you can simply:

function rnatsort(&$a){
   natsort($a);
   $a = array_reverse($a, true);
}
justin at redwiredesign dot com
23-Aug-2003 01:10
One of the things I've needed to do lately is apply natural sorting to a complex array, e.g.:

Array
(
   [0] => Array
       (
           [ID] = 4
           [name] = Fred
       )
      
   [1] => Array
       (
           [ID] = 6
           [name] = Bob
       )
)

where I want to sort the parent array by the child's name. I couldn't see a way of doing this using array_walk, so I've written a simple function to do it. Hopefully someone will find this useful:

/**
 * @return Returns the array sorted as required
 * @param $aryData Array containing data to sort
 * @param $strIndex Name of column to use as an index
 * @param $strSortBy Column to sort the array by
 * @param $strSortType String containing either asc or desc [default to asc]
 * @desc Naturally sorts an array using by the column $strSortBy
 */
function array_natsort($aryData, $strIndex, $strSortBy, $strSortType=false)
{
   //    if the parameters are invalid
   if (!is_array($aryData) || !$strIndex || !$strSortBy)
       //    return the array
       return $aryData;
      
   //    create our temporary arrays
   $arySort = $aryResult = array();
  
   //    loop through the array
   foreach ($aryData as $aryRow)
       //    set up the value in the array
       $arySort[$aryRow[$strIndex]] = $aryRow[$strSortBy];
      
   //    apply the natural sort
   natsort($arySort);

   //    if the sort type is descending
   if ($strSortType=="desc")
       //    reverse the array
       arsort($arySort);
      
   //    loop through the sorted and original data
   foreach ($arySort as $arySortKey => $arySorted)
       foreach ($aryData as $aryOriginal)
           //    if the key matches
           if ($aryOriginal[$strIndex]==$arySortKey)
               //    add it to the output array
               array_push($aryResult, $aryOriginal);

   //    return the return
   return $aryResult;
}
flash at minet dot net
13-Aug-2003 01:27
About the reverse natsort.. Maybe simpler to do :

function strrnatcmp ($a, $b) {
   return strnatcmp ($b, $a);
}
dslicer at maine dot rr dot com
03-Jun-2003 09:41
Something that should probably be documented is the fact that both natsort and natcasesort maintain the key-value associations of the array. If you natsort a numerically indexed array, a for loop will not produce the sorted order; a foreach loop, however, will produce the sorted order, but the indices won't be in numeric order. If you want natsort and natcasesort to break the key-value associations, just use array_values on the sorted array, like so:

natsort($arr);
$arr = array_values($arr);
anonymous at coward dot net
31-May-2003 04:46
Reverse Natsort:

  function rnatsort($a, $b) {
   return -1 * strnatcmp($a, $b);
  }

  usort($arr, "rnatsort");