Web студия "GrandView"
  Главная   Написать Контакты
   
   
О проекте
Руководство php
 

usort

(PHP 3 >= 3.0.3, PHP 4, PHP 5)

usort --  Отсортировать массив по значениям используя пользовательскую функцию для сравнения элементов

Описание

bool usort ( array &array, callback cmp_function )

Эта функция сортирует элементы массива, используя для сравнения значений пользовательскую функцию. Если вам нужно отсортировать массив по необычному критерию, вы должны использовать эту функцию.

Функция, используемая для сравнения, должна возвращать целое число, меньшее, равное или большее нуля, если первый аргумент соответственно меньше, равен или больше второго.

Замечание: Если два элемента исходного массива равны, их порядок в отсортированном массиве неопределён. До PHP 4.0.6 пользовательские функции сохраняли для таких элеменов порядок в оригинальном массиве, но новый алгоритм сортировки, появившейся в версии 4.1.0 больше не использует это решение и не имеет другого эффективного.

Возвращает TRUE в случае успешного завершения или FALSE в случае возникновения ошибки.

Пример 1. Пример использования usort()

<?php
function cmp($a, $b)
{
    if (
$a == $b) {
        return
0;
    }
    return (
$a < $b) ? -1 : 1;
}

$a = array(3, 2, 5, 6, 1);

usort($a, "cmp");

while (list(
$key, $value) = each($a)) {
    echo
"$key: $value
"
;
}
?>

Результат выполнения данного примера:

0: 1
1: 2
2: 3
3: 5
4: 6

Замечание: Очевидно, что для этого тривиального случая более подходит функция sort().

Пример 2. Пример использования функцией usort() многомерных массивов

<?php
function cmp($a, $b)
{
    return
strcmp($a["fruit"], $b["fruit"]);
}

$fruits[0]["fruit"] = "lemons";
$fruits[1]["fruit"] = "apples";
$fruits[2]["fruit"] = "grapes";

usort($fruits, "cmp");

while (list(
$key, $value) = each($fruits)) {
    echo
"\$fruits[$key]: " . $value["fruit"] . "
"
;
}
?>

При сортировке многомерного массива переменные $a и $b содержат ссылки на первые два индекса массива.

Результат выполнения данного примера:

$fruits[0]: apples
$fruits[1]: grapes
$fruits[2]: lemons

Пример 3. Пример использования usort() с методом класса

<?php
class TestObj {
    var
$name;

    function
TestObj($name)
    {
       
$this->name = $name;
    }

   
/* This is the static comparing function: */
   
function cmp_obj($a, $b)
    {
       
$al = strtolower($a->name);
       
$bl = strtolower($b->name);
        if (
$al == $bl) {
            return
0;
        }
        return (
$al > $bl) ? +1 : -1;
    }
}

$a[] = new TestObj("c");
$a[] = new TestObj("b");
$a[] = new TestObj("d");

usort($a, array("TestObj", "cmp_obj"));

foreach (
$a as $item) {
    echo
$item->name . "
"
;
}
?>

Результат выполнения данного примера:

b
c
d

См. также uasort(), uksort(), sort(), asort(), arsort(),ksort(), natsort() и rsort().



Aspell> <uksort
Last updated: Sat, 27 Jan 2007
 
add a note add a note User Contributed Notes
usort
jc dot glombard at gmail dot com
09-Oct-2007 06:20
To svenxy AT nospam gmx net AND rob at digital-crocus dot com

<?php
$zones
=
array(
'192.168.11',
   
'192.169.12',
   
'192.168.13',
   
'192.167.14',
   
'192.168.15',
   
'122.168.16',
   
'192.168.17'
);

natsort($zones);

?>
lite
28-Apr-2007 04:09
<?php
//
class __partial {
    var
$args;
    var
$fn;
   
    function
__partial($args) {
       
$this->fn = array_shift ($args);
       
$this->args = $args;
    }
   
    function
right() {
       
$args = func_get_args ();
       
array_splice ($args, count($args), 0, $this->args);
        return
call_user_func_array ($this->fn, $args);
    }
   
    function
left() {
       
$args = func_get_args ();
       
array_splice ($args, 0, 0, $this->args);
        return
call_user_func_array ($this->fn, $args);
    }
}

function
rcurry () {
   
$closure = &new __partial (func_get_args ());
    return array (
$closure, 'right');
}

function
lcurry () {
   
$closure = &new __partial (func_get_args ());       
    return array (
$closure, 'left');
}

function
compare($a, $b, $key) {
    if (
$a[$key] == $b[$key]) {
        return
0;
    }
    return (
$a[$key] > $b[$key]) ? +1 : -1;
}

function
sortOn ($array, $key) {       
   
usort ($array, rcurry('compare', $key));
    return
$array;
}

$array = array (array ('key' => 1), array ('key' => 5), array ('key' => 3));
print_r (sortOn ($array, 'key'));

?>
15-Mar-2007 06:12
A simple way of having reversed sort order from usort() function without using array_reverse() is to change "greater than" to "lesser that" sign (or vice versa) in the compare function.
rob at digital-crocus dot com
30-Jan-2007 07:44
To svenxy AT nospam gmx net

A faster and clearer way of sorting IP addresses:

<?php
$zones
=
array(
'192.168.11',
   
'192.169.12',
   
'192.168.13',
   
'192.167.14',
   
'192.168.15',
   
'122.168.16',
   
'192.168.17'
);

function
sort_subnets_ip2long($a, $b) {
    return
sprintf("%u", ip2long($a)) - sprintf("%u", ip2long($b));
}

usort($zones, "sort_subnets_ip2long");
?>

Simple testing shows this version is just under twice as fast.

MySQLers: No need to sort this stuff in PHP! Use MySQL's INET_ATON() family of functions to save cycles.
var_dump($zones);
Tim
13-Nov-2006 03:43
If you want to sort an array of strings by the length of its values, you can write a better cmp-function than manually comparing string lengths with if-statements. Unfortunately, I see this bad technique in other people's code. I prefer using this:

<?php
function length_cmp( $a, $b ) {
    return
strlen($a)-strlen($b) ;
}
?>

This function sorts the array in ascending order. If you want to sort descending, simply swap $a and $b either in the parameters or in the subtraction.

-Tim
svenxy AT nospam gmx net
16-Oct-2006 01:58
This will sort subnets correctly:

<?php

$zones
=
array(
'192.168.11',
'192.169.12',
'192.168.13',
'192.167.14',
'192.168.15',
'122.168.16',
'192.168.17'
);

function
sort_subnets ($a, $b) {
   
$a_arr = explode('.', $a);
   
$b_arr = explode('.', $b);
    foreach (
range(0,3) as $i) {
        if (
$a_arr[$i] < $b_arr[$i] ) {
            return -
1;
        }
        elseif (
$a_arr[$i] > $b_arr[$i] ) {
            return
1;
        }
    }
    return -
1;
}

usort($zones, 'sort_subnets');
print
'<pre>';
print_r($zones);
print
'</pre>';

?>
oscahie at gmx dot net
21-Sep-2006 03:51
To markus:

your function can be simplified to:

function SortByName($a, $b) {  
    return strcasecmp($a['name'], $b['name']);
}

usort($test, 'SortByName');

Replace strcasecmp for strcmp if you want case sensitive comparition.
g8z at yahoo dot com
06-Jul-2006 03:53
<?php
/**
This sort function allows you to sort an associative array while "sticking" some fields.

$sticky_fields = an array of fields that should not be re-sorted. This is a method of achieving sub-sorts within contiguous groups of records that have common data in some fields.

For example:

$a = array();

$a []= array(
    'name'         => 'Sam',
    'age'         => 23,
    'hire_date'    => '2004-01-01'
);
$a []= array(
    'name'        => 'Sam',
    'age'        => 44,
    'hire_date'    => '2003-03-23'
);
$a []= array(
    'name'        => 'Jenny',
    'age'        => 20,
    'hire_date' => '2000-12-31'
);
$a []= array(
    'name'        => 'Samantha',
    'age'        => 50,
    'hire_date' => '2000-12-14'
);

$sticky_fields = array( 'name' );
print_r( stickysort( $a, 'age', DESC_NUM, $sticky_fields ) );

OUTPUT:

Array
(
    [0] => Array
        (
            [name] => Sam
            [age] => 44
            [hire_date] => 2003-03-23
        )
    [1] => Array
        (
            [name] => Sam
            [age] => 23
            [hire_date] => 2004-01-01
        )
    [2] => Array
        (
            [name] => Jenny
            [age] => 20
            [hire_date] => 2000-12-31
        )
    [3] => Array
        (
            [name] => Samantha
            [age] => 50
            [hire_date] => 2000-12-14
        )
)

Here's why this is the correct output - the "name" field is sticky, so it cannot change its sort order. Thus, the "age" field is only sorted as a sub-sort within records where "name" is identical. Thus, the "Sam" records are reversed, because 44 > 23, but Samantha remains at the bottom, even though her age is 50. This is a way of achieving "sub-sorts" and "sub-sub-sorts" (and so on) within records of identical data for specific fields.

Courtesy of the $5 Script Archive: http://www.tufat.com
**/

define( 'ASC_AZ', 1000 );
define( 'DESC_AZ', 1001 );
define( 'ASC_NUM', 1002 );
define( 'DESC_NUM', 1003 );

function
stickysort( $arr, $field, $sort_type, $sticky_fields = array() ) {
   
$i = 0;
    foreach (
$arr as $value) {
       
$is_contiguous = true;
        if(!empty(
$grouped_arr)) {
           
$last_value = end($grouped_arr[$i]);

            if(!(
$sticky_fields == array())) {
                foreach (
$sticky_fields as $sticky_field) {
                    if (
$value[$sticky_field] <> $last_value[$sticky_field]) {
                       
$is_contiguous = false;
                        break;
                    }
                }
            }
        }
        if (
$is_contiguous)
           
$grouped_arr[$i][] = $value;
        else
           
$grouped_arr[++$i][] = $value;
    }
   
$code = '';
    switch(
$sort_type) {
        case
ASC_AZ:
           
$code .= 'return strcasecmp($a["'.$field.'"], $b["'.$field.'"]);';
            break;
        case
DESC_AZ:
           
$code .= 'return (-1*strcasecmp($a["'.$field.'"], $b["'.$field.'"]));';
            break;
        case
ASC_NUM:
           
$code .= 'return ($a["'.$field.'"] - $b["'.$field.'"]);';
            break;
        case
DESC_NUM:
           
$code .= 'return ($b["'.$field.'"] - $a["'.$field.'"]);';
            break;
    }

   
$compare = create_function('$a, $b', $code);

    foreach(
$grouped_arr as $grouped_arr_key=>$grouped_arr_value)
       
usort ( $grouped_arr[$grouped_arr_key], $compare );

   
$arr = array();
    foreach(
$grouped_arr as $grouped_arr_key=>$grouped_arr_value)
        foreach(
$grouped_arr[$grouped_arr_key] as $grouped_arr_arr_key=>$grouped_arr_arr_value)
           
$arr[] = $grouped_arr[$grouped_arr_key][$grouped_arr_arr_key];

    return
$arr;
}
?>
Number type ???
16-May-2006 06:13
function arfsort($a,$fl){
  $GLOBALS['__ARFSORT_LIST__'] = $fl;
  usort($a,'arfsort_func');
  return $a;
}

function arfsort_func($a,$b) {
  foreach ($GLOBALS['__ARFSORT_LIST__'] as $f) {
   switch ($f[1]) { // switch on ascending or descending value
     case "d":
       ## Add number type sort
       if (is_numeric($a[$f[0]])) {
            if ($b[$f[0]] < $a[$f[0]]) $strc = -1;
            else if ($b[$f[0]] > $a[$f[0]]) $strc = 1;
            else $strc = 0;
       } else {
         $strc = strcmp($b[$f[0]],$a[$f[0]]);
       }
       if ( $strc != 0 ){
         return $strc;
       }
     break;
     default:
       if (is_numeric($a[$f[0]])) {
              if ($b[$f[0]] > $a[$f[0]]) $strc = -1;
              else if ($b[$f[0]] < $a[$f[0]]) $strc = 1;
              else $strc = 0;
         } else {
           $strc = strcmp($a[$f[0]],$b[$f[0]]);
         }
       if ($strc != 0){
         return $strc;
       }
     break;
   }
  }
  return 0;
}
martin # classaxe com <><
16-May-2006 01:07
Refinement of arfsort to allow per-field sort direction setting:

<?
function arfsort( $a, $fl ){
  $GLOBALS['__ARFSORT_LIST__'] = $fl;
  usort( $a, 'arfsort_func' );
  return $a;
}

// extended to allow sort direction per field sorted against
function arfsort_func( $a, $b ){
  foreach( $GLOBALS['__ARFSORT_LIST__'] as $f ) {
    switch ($f[1]) { // switch on ascending or descending value
      case "d":
        $strc = strcmp( strtolower($b[$f[0]]), strtolower($a[$f[0]]) );
        if ( $strc != 0 ){
          return $strc;
        }
      break;
      default:
        $strc = strcmp( strtolower($a[$f[0]]), strtolower($b[$f[0]]) );
        if ( $strc != 0 ){
          return $strc;
        }
      break;
    }
  }
  return 0;
}

// Just prints out record contents
function show($test) {
  foreach ($test as $key=>$row) {
    print "<li>($key) ".$row['fruit']." is ".$row['type']." at ".$row['time']." on ".$row['date']."</li>\n";
  }
  print "<hr>";
}

// Example usage
$test = array(
  array( 'fruit' => 'Lemon', 'type' => 'sour', 'date' => '2006-05-16', 'time' => '12:36'),
  array( 'fruit' => 'Banana','type' => 'sweet','date' => '2006-05-17','time' => '12:35'),
  array( 'fruit' => 'Apple', 'type' => 'sweet','date' => '2006-05-17','time' => '12:35'),
  array( 'fruit' => 'green apple', 'type' => 'sour', 'date' => '2006-05-16', 'time' => '12:37')
);

show($test);

$order_arr =
  array(
    array('date','d'), // d means decending - swap for 'a' to see effect
    array('time','a'),
    array('fruit','a')
  );

$sorted = arfsort( $test, $order_arr);

show($sorted);
?>
stephen in koto
05-Apr-2006 10:28
For using usort inside a method in an object, where the callback sort method is in the same object, this works:

usort($arr_to_sort, array($this, "sort_terms_by_criteria"));

If you wish to keep elements in their original or reverse order if they are the same length, just don't return zero for items that compare the same. Return a 1 or -1, as appropriate.
sydney at totoche dot org
16-Jan-2006 01:44
Instead of doing  :

$strc = strcmp( strtolower($a[$f]), strtolower($b[$f]) );

you could do this :

$strc = strcasecmp( $a[$f], $b[$f] );

which is more efficient and is does case insensitive comparison according to the current locale.
sean at NOSPAM dot orwd dot com
23-Sep-2005 02:46
In order to get a proper text sorting I have changed the arfsort() function submitted by jonathan(at)inetz(dot)com.

The one line I have changed is:

<?php
$strc
= strcmp( $a[$f], $b[$f] );
?>

to

<?php
$strc
= strcmp( strtolower($a[$f]), strtolower($b[$f]) );
?>

It can now sort correctly for alphabetization.  Capital letters where getting in the way.

Sean
Tim
22-Sep-2005 07:25
A couple examples here advocate the use of 'create_function' for sorting, which is tempting to use because of the limitations of usort. But beware this method -- the function created will NOT be freed at the end of the sorting routine, which creates a memory leak. For this reason, this method should probably never be used.

If you need multi-key or other complex sorting, the fundamental problem is passing in parameters into the comparison routine. I suggest creating a global variable (_array_key_multisort_parameters or something long), and controlling the comparison routine in that way.
markus dot meier at udo dot edu
15-Sep-2005 12:00
@Jeremy Swinborne:

The same can be achieved by usort and will be much faster, too:

<?php

   
function SortByName($a, $b) {
        if (
$a['name'] == $b['name']) return 0;
        return return (
$a['name'] < $b['name']) ? -1 : 1;
    }

   
usort($test, 'SortByName');

?>
markus dot meier at udo dot edu
15-Sep-2005 11:53
gk at lka dot hu:
I've removed a few errors and moved both functions into one. The line with the prefix-check did'nt work right.

<?php
   
function CompareUmlaut($astring, $bstring) {
       
$ALP = "Aa
Новости
11 июля 2007
Сайт запущен
© 2007 info@grandviewstudio.com
Z058440144362 Z348613067571