natsort()
(PHP 4, PHP 5, PHP 7)
用“自然排序”算法对数组排序
说明
natsort(array &$array): bool
本函数实现了一个和人们通常对字母数字字符串进行排序的方法一样的排序算法并保持原有键/值的关联,这被称为“自然排序”。本算法和通常的计算机字符串排序算法(用于sort())的区别见下面示例。
Note:If two members compare as equal, their relative order in the sorted array is undefined.
参数
- $array
输入的 array。
返回值
成功时返回TRUE
,或者在失败时返回FALSE
。
更新日志
版本 | 说明 |
---|---|
5.2.10 | 用零填充的数字字符串(例如'00005')现在本质上会忽略掉填充的前道零。 |
范例
Example #1natsort()基本用法的操作示例
<?php $array1 = $array2 = array("img12.png", "img10.png", "img2.png", "img1.png"); asort($array1); echo "Standard sorting\n"; print_r($array1); natsort($array2); echo "\nNatural order sorting\n"; print_r($array2); ?>
以上例程会输出:
Standard sorting Array ( [3] => img1.png [1] => img10.png [0] => img12.png [2] => img2.png ) Natural order sorting Array ( [3] => img1.png [2] => img2.png [1] => img10.png [0] => img12.png )
For more information see: Martin Pool's» Natural Order String Comparisonpage.
Example #2natsort()examples demonstrating potential gotchas
<?php echo "Negative numbers\n"; $negative = array('-5','3','-2','0','-1000','9','1'); print_r($negative); natsort($negative); print_r($negative); echo "Zero padding\n"; $zeros = array('09', '8', '10', '009', '011', '0'); print_r($zeros); natsort($zeros); print_r($zeros); ?>
以上例程会输出:
Negative numbers Array ( [0] => -5 [1] => 3 [2] => -2 [3] => 0 [4] => -1000 [5] => 9 [6] => 1 ) Array ( [2] => -2 [0] => -5 [4] => -1000 [3] => 0 [6] => 1 [1] => 3 [5] => 9 ) Zero padding Array ( [0] => 09 [1] => 8 [2] => 10 [3] => 009 [4] => 011 [5] => 0 ) Array ( [5] => 0 [1] => 8 [3] => 009 [0] => 09 [2] => 10 [4] => 011 )
参见
natcasesort()
用“自然排序”算法对数组进行不区分大小写字母的排序- 数组排序函数对比
strnatcmp()
使用自然排序算法比较字符串strnatcasecmp()
使用“自然顺序”算法比较字符串(不区分大小写)
There's no need to include your own API code to natsort an associative array by key. PHP's in-built functions (other than natsort) can do the job just fine: <?php uksort($myArray, "strnatcmp"); ?>
About the reverse natsort.. Maybe simpler to do : function strrnatcmp ($a, $b) { return strnatcmp ($b, $a); }
Be careful of the new behaviour in 5.2.10 version. See the following sample: <?php $array = array('1 bis', '10 ter', '0 PHP', '0', '01', '01 Ver', '0 ', '1 ', '1'); natsort($array); echo '<pre>'; print_r($array); echo '</pre>'; ?> 5.2.6-1 will output: Array ( [3] => 0 [6] => 0 [2] => 0 OP [4] => 01 [5] => 01 Ver [8] => 1 [7] => 1 [0] => 1 bis [1] => 10 ter ) 5.2.10 will output: Array ( [6] => 0 [3] => 0 [8] => 1 [4] => 01 [7] => 1 [5] => 01 Ver [0] => 1 bis [1] => 10 ter [2] => 0 OP ) Greetings
This made me waste a lot of my precious youth ... natsort() is buggy if all numbers don't have the same number of decimal places. (php 5.6.4-4ubuntu6.2) <?php $different_decimal_places_in_values = array('D'=>'13.59', '14.6' => '14.6', 'C-' => '14.19'); natsort($a); var_dump($a); /*echoes array(3) { 'D' => string(5) "13.59" '14.6' => string(4) "14.6" <----------- badly ordered 'C-' => string(5) "14.19" }*/ ?> While this <?php $same_num_decimal_places_in_values = array('D'=>'13.59', '14.6' => '14.60', 'C-' => '14.19'); natsort($a); var_dump($a); /*echoes array(3) { 'D' => string(5) "13.59" 'C-' => string(5) "14.19" '14.6' => string(5) "14.60" <--------- that is the correct position } */ ?>
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.
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; } ?>
To make a reverse function, you can simply: function rnatsort(&$a){ natsort($a); $a = array_reverse($a, true); }
Reverse Natsort: function rnatsort($a, $b) { return -1 * strnatcmp($a, $b); } usort($arr, "rnatsort");
Note: negatives number. <?php $a = array(-5,-2,3,9); natsort($a); print_r($a); ?> Will output: Array ( [1] => -2 [0] => -5 [2] => 3 [3] => 9 )
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!
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; } ?>
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'
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
As noted in other notes, natsort() does _not_ always return the expected sort order. It seems especially buggy when decimals or 0 padding is used. I've filed this bug report on the issue: https://bugs.php.net/bug.php?id=74672
$array1 = $array2 = array('IMG0.png', 'img12.png', 'img10.png', 'img2.png', 'img1.png', 'IMG3.png'); natsort($array1); echo "\n natsort(); \n"; print_r($array1); sort($array2, SORT_NATURAL); echo "\n sort() with SORT_NATURAL Option\n"; print_r($array2); Ouput: natsort(); Array ( [0] => IMG0.png [5] => IMG3.png [4] => img1.png [3] => img2.png [2] => img10.png [1] => img12.png ) sort() with SORT_NATURAL Option Array ( [0] => IMG0.png [1] => IMG3.png [2] => img1.png [3] => img2.png [4] => img10.png [5] => img12.png ) as we can see it's the same values but not the same keys, and also it's same for sort($array1, SORT_NATURAL | SORT_FLAG_CASE); and natcasesort($array2)
To naturally sort by array key, the uksort function can be used. <?php echo "Sort by keys\n"; $smoothie = array('orange' => 1, 'apple' => 1, 'yogurt' => 4, 'banana' => 4); print_r($smoothie); uksort( $smoothie, 'strnatcmp'); print_r($smoothie) ?> Output: Sort by keys Array ( [orange] => 1 [apple] => 1 [yogurt] => 4 [banana] => 4 ) Array ( [apple] => 1 [banana] => 4 [orange] => 1 [yogurt] => 4 ) See http://php.net/manual/en/function.uksort.php for more information about uksort and http://php.net/strnatcmp for usage of strnatcmp.
there is another rnatsort function lower on the page, but it didn't work in the context i needed it in. reasoning for this: sorting naturally via the keys of an array, but needing to reverse the order. function rnatsort ( &$array = array() ) { $keys = array_keys($array); natsort($keys); $total = count($keys) - 1; $temp1 = array(); $temp2 = array(); // assigning original keys to an array with a backwards set of keys, to use in krsort(); foreach ( $keys as $key ) { $temp1[$total] = $key; --$total; } ksort($temp1); // setting the new array, with the order from the krsort() and the values of original array. foreach ( $temp1 as $key ) { $temp2[$key] = $array[$key]; } $array = $temp2; }
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; } ?>
natsort might not act like you would expect with zero padding, heres a quick sample. <?php $array = array('09', '8', '10', '009', '011'); natsort($array); ?> /* Array ( [3] => 009 [4] => 011 [0] => 09 [1] => 8 [2] => 10 ) */