Làm thế nào để sắp xếp một mảng các mảng kết hợp theo giá trị của một khóa đã cho trong PHP?


446

Cho mảng này:

$inventory = array(

   array("type"=>"fruit", "price"=>3.50),
   array("type"=>"milk", "price"=>2.90),
   array("type"=>"pork", "price"=>5.43),

);

Tôi muốn sắp xếp $inventorycác yếu tố theo giá để có được:

$inventory = array(

   array("type"=>"pork", "price"=>5.43),
   array("type"=>"fruit", "price"=>3.50),
   array("type"=>"milk", "price"=>2.90),

);

Tôi có thể làm cái này như thế nào?


Câu trả lời:


605

Bạn nói đúng, chức năng bạn đang tìm kiếm là array_multisort().

Dưới đây là một ví dụ được lấy thẳng từ hướng dẫn và điều chỉnh cho trường hợp của bạn:

$price = array();
foreach ($inventory as $key => $row)
{
    $price[$key] = $row['price'];
}
array_multisort($price, SORT_DESC, $inventory);

Kể từ phiên bản PHP 5.5.0, bạn có thể sử dụng mảng_column () thay vì foreach đó

$price = array_column($inventory, 'price');

array_multisort($price, SORT_DESC, $inventory);

5
Mặc dù điều này chắc chắn là đắt hơn so với các lựa chọn thay thế.
Matt

5
Đắt hơn? Thật kỳ lạ, trên máy của tôi (chạy PHP 5.3.1-dev) Array_multisort () nhanh hơn vài phần trăm trên các mảng nhỏ và nhanh hơn tới 100 lần trên các mảng lớn (hơn 100 phần tử)
Josh Davis

3
Nó không yêu cầu bất kỳ thay đổi để làm việc với các phím số. Nếu bạn gặp phải một lỗi hoặc hành vi kỳ lạ liên quan đến các phím số, hãy đăng nó dưới dạng một câu hỏi mới.
Josh Davis

4
mảng_multisort có một vấn đề lớn: nó không duy trì khóa gốc.
máy móc

1
@machineaddict nó không duy trì các khóa kết hợp.
Matej Svajger

317

7+ PHP

Kể từ PHP 7, điều này có thể được thực hiện chính xác bằng cách sử dụng usortmột hàm ẩn danh sử dụng toán tử tàu vũ trụ để so sánh các phần tử.

Bạn có thể thực hiện một loại tăng dần như thế này:

usort($inventory, function ($item1, $item2) {
    return $item1['price'] <=> $item2['price'];
});

Hoặc một loại giảm dần như thế này:

usort($inventory, function ($item1, $item2) {
    return $item2['price'] <=> $item1['price'];
});

Để hiểu cách thức hoạt động của nó, lưu ý rằng usortcó một hàm so sánh do người dùng cung cấp phải hoạt động như sau (từ các tài liệu):

Hàm so sánh phải trả về một số nguyên nhỏ hơn, bằng hoặc lớn hơn 0 nếu đối số thứ nhất được coi là tương ứng nhỏ hơn, bằng hoặc lớn hơn số thứ hai.

Và cũng lưu ý rằng <=>, người vận hành tàu vũ trụ,

trả về 0 nếu cả hai toán hạng đều bằng nhau, 1 nếu bên trái lớn hơn và -1 nếu bên phải lớn hơn

đó chính xác là những gì usortcần. Trên thực tế, gần như toàn bộ lời biện minh được đưa ra để thêm <=>vào ngôn ngữ trong https://wiki.php.net/rfc/combined-comparison-operator là nó

làm cho văn bản gọi lại để sử dụng usort()dễ dàng hơn


PHP 5.3 trở lên

PHP 5.3 đã giới thiệu các hàm ẩn danh, nhưng chưa có toán tử tàu vũ trụ. Chúng ta vẫn có thể sử dụng usortđể sắp xếp mảng của mình, nhưng nó dài dòng hơn và khó hiểu hơn:

usort($inventory, function ($item1, $item2) {
    if ($item1['price'] == $item2['price']) return 0;
    return $item1['price'] < $item2['price'] ? -1 : 1;
});

Lưu ý rằng mặc dù khá phổ biến đối với các bộ so sánh xử lý các giá trị nguyên để trả về sự khác biệt của các giá trị, như $item2['price'] - $item1['price'], chúng ta không thể làm điều đó một cách an toàn trong trường hợp này. Điều này là do giá là các số dấu phẩy động trong ví dụ của người hỏi, nhưng hàm so sánh chúng ta chuyển qua usortphải trả về số nguyên usortđể hoạt động chính xác:

Trả về các giá trị không nguyên từ hàm so sánh, chẳng hạn như float, sẽ dẫn đến việc truyền nội bộ thành số nguyên của giá trị trả về của hàm gọi lại. Vì vậy, các giá trị như 0,99 và 0,1 sẽ được chuyển thành giá trị nguyên bằng 0, sẽ so sánh các giá trị đó bằng nhau.

Đây là một cái bẫy quan trọng cần lưu ý khi sử dụng usorttrong PHP 5.x! Phiên bản gốc của câu trả lời này đã mắc lỗi này và tôi đã tích lũy được mười lượt tán thành qua hàng ngàn lượt xem mà không ai nhận ra lỗi nghiêm trọng. Sự dễ dàng mà những người thiếu như tôi có thể làm hỏng các chức năng so sánh chính xác là lý do mà toán tử tàu vũ trụ dễ sử dụng hơn đã được thêm vào ngôn ngữ trong PHP 7.


8
Xin lỗi, nhưng cách tiếp cận này xóa các khóa chuỗi khỏi mảng kết hợp. Thay vào đó, nên sử dụng chức năng "uasort".
Matteo-SoftNet

8
@DotMat Thú vị - Tôi không biết về uasort. Sau khi xem các tài liệu, câu trả lời này vẫn đúng trong trường hợp này . Trong ví dụ của OP, mảng được sắp xếp có các chỉ mục số liên tiếp thay vì chỉ mục chuỗi, do đó usortphù hợp hơn. Sử dụng uasorttrên một mảng được lập chỉ mục tuần tự sẽ dẫn đến một mảng được sắp xếp không được sắp xếp theo các chỉ số số của nó, do đó phần tử đầu tiên nhìn thấy trong một foreachvòng lặp là không $your_array[0]có khả năng là hành vi mong muốn.
Đánh dấu Amery

99

Trong khi những người khác đã đề xuất chính xác việc sử dụng array_multisort(), vì một số lý do dường như không có câu trả lời nào thừa nhận sự tồn tại của array_column(), điều này có thể đơn giản hóa rất nhiều giải pháp. Vì vậy, đề nghị của tôi sẽ là:

array_multisort(array_column($inventory, 'price'), SORT_DESC, $inventory);

1
Vì một số lý do, tôi không thể làm cho nó hoạt động với các chuỗi có chữ cái dưới / trên. Ngay cả khi sử dụng SORT_FLAG_CASE. Sau đây làm việc để so sánh chuỗi cho tôi: Array_multisort (Array_map (strtolower, Array_column ($ ipr_projects, 'Name')), SORT_ASC, $ ipr_projects);
Pabamato

8
Đây là câu trả lời thanh lịch nhất. Nên được đánh giá cao hơn nhiều!
Armin Hierstetter

3
Theo tôi, ngắn nhất và dễ nhất, được chấp nhận theo ý kiến ​​của tôi
StudioX

1
Thứ tốt ở đây Làm việc hoàn hảo cho tôi!
Funk Doc

Làm việc như một bùa mê, ty
Leif_Lundberg 16/12/19

42

Vì các thành phần mảng của bạn là các mảng với các khóa chuỗi, nên cách tốt nhất của bạn là xác định hàm so sánh tùy chỉnh. Nó khá nhanh và dễ làm. Thử cái này:

function invenDescSort($item1,$item2)
{
    if ($item1['price'] == $item2['price']) return 0;
    return ($item1['price'] < $item2['price']) ? 1 : -1;
}
usort($inventory,'invenDescSort');
print_r($inventory);

Sản xuất như sau:

Array
(
    [0] => Array
        (
            [type] => pork
            [price] => 5.43
        )

    [1] => Array
        (
            [type] => fruit
            [price] => 3.5
        )

    [2] => Array
        (
            [type] => milk
            [price] => 2.9
        )

)

4
Kết hợp với một số ý kiến ​​khác ở đây (chức năng ẩn danh uasort và nội tuyến), bạn có được phần mềm này:uasort( $inventory, function ($a, $b) { if ( $a==$b ) return 0; else return ($a > $b) ? -1 : 1; });
Alan Porter

@AlanPorter usortcó vẻ phù hợp hơn là uasortsắp xếp một mảng với các phím số liên tiếp. Kết thúc với một mảng trong đó phần tử đầu tiên nằm trong chỉ mục 1và phần tử thứ hai nằm trong chỉ mục 0là hành vi kỳ lạ và một cái bẫy chắc chắn cho những người không quen thuộc với các chi tiết của mảng PHP; usortcung cấp cho bạn đầu ra mà bạn mong đợi bằng trực giác.
Đánh dấu Amery

25

Tôi đã kết thúc về điều này:

function sort_array_of_array(&$array, $subfield)
{
    $sortarray = array();
    foreach ($array as $key => $row)
    {
        $sortarray[$key] = $row[$subfield];
    }

    array_multisort($sortarray, SORT_ASC, $array);
}

Chỉ cần gọi hàm, truyền mảng và tên của trường của mảng cấp hai. Giống:

sort_array_of_array($inventory, 'price');

1
Hà! Tôi chỉ thực hiện khá chính xác điều tương tự và sẽ đăng nhưng thấy ... của bạn được nâng cấp.
Rob Evans

1
Hạ cấp vì đây chính xác là giải pháp tương tự mà Josh Davis đã đăng nhiều năm trước đó.
Đánh dấu Amery

Không đồng ý ... Tôi không nói đó là một giải pháp khác, tôi chỉ nói rằng tôi đã kết thúc với giải pháp này và cung cấp một chức năng hoạt động đầy đủ.
Danielzt

1
@MarkAmery Tôi thích câu trả lời có trong các hàm. Nó khuyến khích các mục sư sao chép sử dụng các chức năng và hy vọng viết ít mã spaghetti hơn.
Ngỗng

19

Bạn có thể sử dụng usortvới chức năng ẩn danh, vd

usort($inventory, function ($a, $b) { return strnatcmp($a['price'], $b['price']); });

Phiên bản PHP 5> = 5.5.0, PHP 7 cho những người như tôi thực sự muốn điều này hoạt động với họ ..
Matt P

1
Đáng chú ý rằng strnatcmp, có nghĩa là để so sánh các chuỗi, dường như hoạt động tốt ở đây. Rõ ràng "trật tự tự nhiên" mà nó thực hiện bao gồm sắp xếp các chuỗi số chứ không phải theo từ vựng.
Đánh dấu Amery

10
$inventory = 
    array(array("type"=>"fruit", "price"=>3.50),
          array("type"=>"milk", "price"=>2.90),
          array("type"=>"pork", "price"=>5.43),
          );

function pricesort($a, $b) {
  $a = $a['price'];
  $b = $b['price'];
  if ($a == $b)
    return 0;
  return ($a > $b) ? -1 : 1;
}

usort($inventory, "pricesort");
// uksort($inventory, "pricesort");

print("first: ".$inventory[0]['type']."\n\n");
// for usort(): prints milk (item with lowest price)
// for uksort(): prints fruit (item with key 0 in the original $inventory)

// foreach prints the same for usort and uksort.
foreach($inventory as $i){
  print($i['type'].": ".$i['price']."\n");
}

đầu ra:

first: pork

pork: 5.43
fruit: 3.5
milk: 2.9

6

Từ Sắp xếp một mảng các mảng kết hợp theo giá trị của khóa đã cho trong php :

uasort ( http://php.net/uasort ) cho phép bạn sắp xếp một mảng theo chức năng được xác định của riêng bạn. Trong trường hợp của bạn, điều đó thật đơn giản:

$array = array(
  array('price'=>'1000.50','product'=>'test1'),
  array('price'=>'8800.50','product'=>'test2'),
  array('price'=>'200.0','product'=>'test3')
);

function cmp($a, $b) {
  return $a['price'] > $b['price'];
}

uasort($array, "cmp");

1
Câu trả lời này xuất hiện trong hàng đánh giá chất lượng thấp, có lẽ vì bạn không cung cấp bất kỳ lời giải thích nào về mã. Nếu mã này trả lời câu hỏi, hãy xem xét thêm một số văn bản giải thích mã trong câu trả lời của bạn. Bằng cách này, bạn có nhiều khả năng nhận được nhiều upvote hơn - và giúp người hỏi tìm hiểu điều gì đó mới.
LMO

1
hmpf. đây là câu trả lời hay nhất tho.
phổ biến

1
-1; các cmpchức năng ở đây là sai. Nó được cho là trả về "một số nguyên nhỏ hơn, bằng hoặc lớn hơn 0 nếu đối số thứ nhất được coi là tương ứng nhỏ hơn, bằng hoặc lớn hơn số thứ hai" nhưng thay vào đó trả về truehoặc false. Dường như, đáng chú ý, đối với công việc dù sao - có lẽ bởi vì việc triển khai usortvà bạn bè hiện tại đối xử với các trường hợp "ít hơn" và "bằng" một cách giống hệt nhau - nhưng đừng tin vào việc tiếp tục làm việc trong các phiên bản PHP trong tương lai. Nếu họ cố gắng để sắp xếp ổn định (nghĩa là không di chuyển xung quanh các phần tử bằng nhau một cách không cần thiết), điều này sẽ bị phá vỡ.
Đánh dấu Amery

Ngoài ra, usortsẽ phù hợp hơn uasortở đây, vì uasortduy trì sự liên kết giữa các khóa và giá trị gây nhầm lẫn và bất ngờ khi thực hiện với một mảng số liên tiếp. Chẳng hạn, các chỉ số $arrayở trên sau khi gọi uasortlà 2, 0 và 1, theo thứ tự đó. Trừ khi bạn vì một số lý do muốn điều đó, có lẽ bạn sẽ thấy thoải mái hơn khi sử dụng usort, điều này sẽ giới thiệu lại mảng cũng như sắp xếp lại nó.
Đánh dấu Amery

5

Đã được thử nghiệm trên 100 000 bản ghi: Thời gian tính bằng giây (tính bằng microtime funciton). Chỉ cho các giá trị duy nhất về sắp xếp các vị trí quan trọng.

Giải pháp về chức năng của @Josh Davis: Thời gian dành : 1.5768740177155

Giải pháp khai thác: Thời gian dành : 0,094044923782349

Giải pháp:

function SortByKeyValue($data, $sortKey, $sort_flags=SORT_ASC)
{
    if (empty($data) or empty($sortKey)) return $data;

    $ordered = array();
    foreach ($data as $key => $value)
        $ordered[$value[$sortKey]] = $value;

    ksort($ordered, $sort_flags);

    return array_values($ordered); *// array_values() added for identical result with multisort*
}

7
Tuy nhiên, yêu cầu đối với các khóa sắp xếp duy nhất là loại bộ ngắt thỏa thuận. Nếu bạn có các giá trị sắp xếp duy nhất có thể là các khóa, nó đặt ra câu hỏi: tại sao không chỉ đơn giản là xây dựng mảng với các khóa đó để bắt đầu? Trong kịch bản của OP, thật khó để tưởng tượng rằng hai mặt hàng có cùng giá sẽ là không thể . Điều đó trong tâm trí, sử dụng giải pháp này sẽ khiến các mục từ mảng biến mất một cách bí ẩn và âm thầm khỏi tập kết quả được sắp xếp.
Chris Baker

@Chris Baker, bạn nói đúng. Điều này chỉ hoạt động cho các giá trị duy nhất. Nhưng giải pháp này hoạt động rất nhanh, vì vậy tốc độ là lý do tạo ra và sử dụng nó. Hiện tại có thể nó không thực tế, cần phải thử nghiệm nó với PHP 7.1.x.
Nefelim

4

Tôi sử dụng uasortnhư thế này

<?php
$users = [
    [
        'username' => 'joe',
        'age' => 11
    ],
    [
        'username' => 'rakoto',
        'age' => 21
    ],
    [
        'username' => 'rabe',
        'age' => 17
    ],
    [
        'username' => 'fy',
        'age' => 19
    ],    
];


uasort($users, function ($item, $compare) {
    return $item['username'] >= $compare['username']; 
});

var_dump($users);

3

Chức năng này có thể sử dụng lại:

function usortarr(&$array, $key, $callback = 'strnatcasecmp') {
    uasort($array, function($a, $b) use($key, $callback) {
        return call_user_func($callback, $a[$key], $b[$key]);
    });
}

Nó hoạt động tốt trên các giá trị chuỗi theo mặc định, nhưng bạn sẽ phải gọi lại hàm cho một hàm so sánh số nếu tất cả các giá trị của bạn là số.


Bạn gọi cái này usortarrnhưng sau đó gọi uasortthay usort; có lẽ hơi khó hiểu Điều thứ hai là - trong trường hợp một mảng liên tiếp với các chỉ số bằng số, giống như một mảng được thể hiện trong câu hỏi - có lẽ là những gì bạn thực sự muốn.
Đánh dấu Amery

2

Bạn có thể thử xác định chức năng so sánh của riêng mình và sau đó sử dụng usort .


Đúng. Tôi sẽ làm điều đó nếu tôi không thể tìm ra giải pháp. Tôi khá chắc chắn rằng có một số tham số kỳ lạ mà bạn có thể thêm vào một trong những loại để thực hiện điều này. Cảm ơn những suy nghĩ của bạn mặc dù!
Matt

2

Đây là một phương pháp mà tôi đã tìm thấy từ lâu và làm sạch một chút. Điều này hoạt động rất tốt, và có thể nhanh chóng thay đổi để chấp nhận các đối tượng.

/**
 * A method for sorting arrays by a certain key:value.
 * SortByKey is the key you wish to sort by
 * Direction can be ASC or DESC.
 *
 * @param $array
 * @param $sortByKey
 * @param $sortDirection
 * @return array
 */
private function sortArray($array, $sortByKey, $sortDirection) {

    $sortArray = array();
    $tempArray = array();

    foreach ( $array as $key => $value ) {
        $tempArray[] = strtolower( $value[ $sortByKey ] );
    }

    if($sortDirection=='ASC'){ asort($tempArray ); }
        else{ arsort($tempArray ); }

    foreach ( $tempArray as $key => $temp ){
        $sortArray[] = $array[ $key ];
    }

    return $sortArray;

}

để thay đổi phương thức sắp xếp các đối tượng, chỉ cần thay đổi dòng sau:

$tempArray[] = strtolower( $value[ $sortByKey ] ); đến $tempArray[] = strtolower( $value->$sortByKey );

Để chạy phương thức đơn giản là làm

sortArray($inventory,'price','ASC');


Cách tiếp cận này có hiệu quả, nhưng hơi ngắn gọn một chút so với câu trả lời của Josh Davis (với array_multisort) hoặc của tôi (với usort) và dường như không mang lại lợi thế nào cho chúng khi trao đổi.
Mark Amery

1
//Just in one line custom function
function cmp($a, $b)
{
return (float) $a['price'] < (float)$b['price'];
}
@uasort($inventory, "cmp");
print_r($inventory);

//result

Array
(
[2] => Array
    (
        [type] => pork
        [price] => 5.43
    )

[0] => Array
    (
        [type] => fruit
        [price] => 3.5
    )

[1] => Array
    (
        [type] => milk
        [price] => 2.9
    )

)

1

thử cái này:

$prices = array_column($inventory, 'price');
array_multisort($prices, SORT_DESC, $inventory);
print_r($inventory);

Xin chào và chào mừng đến stackoverflow, và cảm ơn bạn đã trả lời. Mặc dù mã này có thể trả lời câu hỏi, bạn có thể xem xét thêm một số giải thích cho vấn đề bạn đã giải quyết và cách bạn giải quyết nó không? Điều này sẽ giúp người đọc trong tương lai hiểu câu trả lời của bạn tốt hơn và học hỏi từ nó.
Plutian

0

Hoàn thành chức năng động Tôi đã nhảy vào đây để sắp xếp mảng kết hợp và tìm thấy chức năng tuyệt vời này trên http://php.net/manual/en/feft.sort.php . Hàm này rất động sắp xếp theo thứ tự tăng dần và giảm dần với khóa được chỉ định.

Hàm đơn giản để sắp xếp một mảng bằng một khóa cụ thể. Duy trì liên kết chỉ số

<?php

function array_sort($array, $on, $order=SORT_ASC)
{
    $new_array = array();
    $sortable_array = array();

    if (count($array) > 0) {
        foreach ($array as $k => $v) {
            if (is_array($v)) {
                foreach ($v as $k2 => $v2) {
                    if ($k2 == $on) {
                        $sortable_array[$k] = $v2;
                    }
                }
            } else {
                $sortable_array[$k] = $v;
            }
        }

        switch ($order) {
            case SORT_ASC:
                asort($sortable_array);
            break;
            case SORT_DESC:
                arsort($sortable_array);
            break;
        }

        foreach ($sortable_array as $k => $v) {
            $new_array[$k] = $array[$k];
        }
    }

    return $new_array;
}

$people = array(
    12345 => array(
        'id' => 12345,
        'first_name' => 'Joe',
        'surname' => 'Bloggs',
        'age' => 23,
        'sex' => 'm'
    ),
    12346 => array(
        'id' => 12346,
        'first_name' => 'Adam',
        'surname' => 'Smith',
        'age' => 18,
        'sex' => 'm'
    ),
    12347 => array(
        'id' => 12347,
        'first_name' => 'Amy',
        'surname' => 'Jones',
        'age' => 21,
        'sex' => 'f'
    )
);

print_r(array_sort($people, 'age', SORT_DESC)); // Sort by oldest first
print_r(array_sort($people, 'surname', SORT_ASC)); // Sort by surname

-1
$arr1 = array(

    array('id'=>1,'name'=>'aA','cat'=>'cc'),
    array('id'=>2,'name'=>'aa','cat'=>'dd'),
    array('id'=>3,'name'=>'bb','cat'=>'cc'),
    array('id'=>4,'name'=>'bb','cat'=>'dd')
);

$result1 = array_msort($arr1, array('name'=>SORT_DESC);

$result2 = array_msort($arr1, array('cat'=>SORT_ASC);

$result3 = array_msort($arr1, array('name'=>SORT_DESC, 'cat'=>SORT_ASC));


function array_msort($array, $cols)
{
    $colarr = array();
    foreach ($cols as $col => $order) {
    $colarr[$col] = array();
    foreach ($array as $k => $row) { $colarr[$col]['_'.$k] = strtolower($row[$col]); }
}

$eval = 'array_multisort(';

foreach ($cols as $col => $order) {
    $eval .= '$colarr[\''.$col.'\'],'.$order.',';
}

$eval = substr($eval,0,-1).');';
eval($eval);
$ret = array();
foreach ($colarr as $col => $arr) {
    foreach ($arr as $k => $v) {
        $k = substr($k,1);
        if (!isset($ret[$k])) $ret[$k] = $array[$k];
        $ret[$k][$col] = $array[$k][$col];
    }
}
return $ret;


} 

Mặc dù đoạn mã này có thể giải quyết câu hỏi, bao gồm một lời giải thích thực sự giúp cải thiện chất lượng bài đăng của bạn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho độc giả trong tương lai và những người đó có thể không biết lý do cho đề xuất mã của bạn. Xin vui lòng cố gắng không làm đông mã của bạn với các bình luận giải thích, vì điều này làm giảm khả năng đọc của cả mã và các giải thích!
Tạm biệt StackExchange

-5

thử cái này:

asort($array_to_sort, SORT_NUMERIC);

để tham khảo xem điều này: http://php.net/manual/en/feft.asort.php

xem các cờ sắp xếp khác nhau ở đây: http://www.php.net/manual/en/feft.sort.php


điều này sẽ không hoạt động cho các mảng đa chiều, nhưng chỉ giúp tôi giải quyết vấn đề khác, cảm ơn :)
schellmax

4
Điều này không thể được sử dụng để sắp xếp danh sách từ điển theo một khóa từ điển cụ thể và do đó không trả lời câu hỏi được đặt ra.
Đánh dấu Amery
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.