連想配列の配列をPHPの特定のキーの値でソートする方法は?


446

この配列を考えると:

$inventory = array(

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

);

私は$inventoryの要素を価格でソートして取得します:

$inventory = array(

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

);

これどうやってするの?


回答:


605

あなたが正しい、あなたが探している機能はですarray_multisort()

これはマニュアルから直接取られ、あなたのケースに適応された例です:

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

PHP 5.5.0以降では、foreachの代わりにarray_column()を使用できます

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

array_multisort($price, SORT_DESC, $inventory);

5
これは間違いなく他のものよりも高価です。
マット

5
もっと高い?奇妙なことに、私のマシン(PHP 5.3.1-devを実行)では、array_multisort()は小さな配列で数パーセント速く、大きな配列(100以上の要素)で最大100倍速くなります
Josh Davis

3
数値キーを操作するために変更を加える必要はありません。テンキーに関連するバグや奇妙な動作に遭遇した場合は、新しい質問として投稿してください。
Josh Davis

4
array_multisortには大きな問題があります。元のキーを維持しません。
machineaddict 2013

1
@machineaddictそれは連想キーを維持します。
Matej Svajger、2015

317

PHP 7以降

PHP 7以降、これは、要素を比較するために宇宙船演算子を使用usortする匿名関数を使用して簡潔に行うことができます。

次のように昇順で並べ替えることができます。

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

または、次のような降順ソート:

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

これがどのように機能するかを理解するためにusort、はユーザー提供の比較関数を使用することに注意してください。比較関数は(ドキュメントから)次のように動作する必要があります。

最初の引数がそれぞれ2番目の引数より小さい、等しい、または大きいと見なされる場合、比較関数はゼロより小さい、等しい、またはより大きい整数を返す必要があります。

また<=>、宇宙船のオペレーターである

両方のオペランドが等しい場合は0を返し、左が大きい場合は1を返し、右が大きい場合は-1を返します

これはまさにusort必要なものです。実際、https://wiki.php.net/rfc/combined-comparison-operator<=>の言語に追加することで与えられる正当化のほとんどすべては、

で使用するための順序付けコールバックの記述をusort()簡単にします


PHP 5.3以降

PHP 5.3は匿名関数を導入しましたが、まだ宇宙船演算子はありません。usort配列をソートするために引き続き使用できますが、少し冗長で理解が難しくなります。

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

整数値を扱うコンパレータがちょうど値の差を返すようにするために、それはかなり一般的ですが、のようなことに注意$item2['price'] - $item1['price']、我々はできません安全に、この場合のことを行います。これは、質問の質問者の例では価格が浮動小数点数であるためです。ただし、渡される比較関数は、適切に機能usortするためusortに整数を返す必要があります。

戻り非整数、フロートとして、比較関数の値を、コールバック関数の戻り値の整数の内部キャストをもたらすであろう。したがって、0.99や0.1などの値はどちらも0の整数値にキャストされ、これらの値は等しいと比較されます。

これは、usortPHP 5.xで使用する際に留意すべき重要なトラップです。この回答の私の元のバージョンはこの間違いを犯しましたが、重大なバグに誰も気付かずに、何千ものビューに対して10の賛成票を獲得したようです。私のようなウィットウィットがコンパレーター機能を台無しにしてしまうことが容易であることが、PHP 7の言語に使いやすい宇宙船オペレーターが追加された理由です。


8
申し訳ありませんが、この方法では連想配列から文字列キーが削除されます。代わりに「uasort」関数を使用する必要があります。
Matteo-SoftNet 14年

8
@DotMat興味深い-私は知らなかったuasort。しかし、ドキュメントを見た後、この場合でもこの答えは正しいです。OPの例では、ソートされる配列には文字列インデックスではなく連続した数値インデックスがあるためusort、より適切です。uasort順次インデックス付けされた配列で使用すると、数値インデックスで並べ替えられていないソートされた配列が生成され、foreachループ内で最初に見られる要素がになら$your_array[0]ないため、望ましい動作とは言えません。
Mark Amery 2014年

99

他の人はの使用を正しく提案していますがarray_multisort()、何らかの理由で、の存在を認める回答がないようでarray_column()、ソリューションを大幅に簡略化できます。だから私の提案は:

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

1
どういうわけか、小文字/大文字の文字列でそれを動作させることができませんでした。SORT_FLAG_CASEを使用する場合でも。次は、文字列比較で機能しました:array_multisort(array_map(strtolower、array_column($ ipr_projects、 'Name'))、SORT_ASC、$ ipr_projects);
Pabamato

8
これは最もエレガントな答えです。はるかに高く評価されるべきです!
Armin Hierstetter、2018

3
最短で最も簡単なもの、私の意見では受け入れたもの
StudioX

1
ここに良いもの。私には完璧に働きました!
Funk Doc

チャームのように働いた、ty
Leif_Lundberg

42

配列要素は文字列キーを持つ配列自体なので、最善の策はカスタム比較関数を定義することです。それはかなり迅速で簡単です。これを試して:

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

以下を生成します。

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

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

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

)

4
ここでは他のコメント(uasortとインライン無名関数)の一部と組み合わせることで、あなたはこのワンライナーを得る:uasort( $inventory, function ($a, $b) { if ( $a==$b ) return 0; else return ($a > $b) ? -1 : 1; });
アラン・ポーター

@AlanPorter usortuasort、連続した数値キーで配列をソートするよりも適切と思われます。最初の要素がインデックスに1あり、2番目の要素がインデックスにある配列になってしまうの0は奇妙な動作であり、PHPの配列の詳細に慣れていない人にとっては確実な罠です。usort直感的に期待する出力が得られます。
Mark Amery、2015年

25

私はこれで終わりました:

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

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

配列と第2レベルの配列のフィールドの名前を渡して、関数を呼び出すだけです。お気に入り:

sort_array_of_array($inventory, 'price');

1
ハ!まったく同じものを作って投稿するつもりでしたが、あなたのものを見ました...賛成です。
Rob Evans 14

1
これは、Josh Davisが数年前に投稿したソリューションとまったく同じであるため、反対票を投じます。
Mark Amery、2015年

そう思わない...私はそれが別の解決策であるとは言いませんでした、私はこの解決策で終わり、完全に機能する機能を与えると言っただけです。
Danielzt 2017年

1
@MarkAmery私は関数に含まれる答えを好みます。それはコピー貼り付け者が関数を使用することを奨励し、うまくいけばより少ないスパゲッティコードを書くでしょう。
2017年

19

usort匿名関数で使用できます。

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

バージョンPHP 5> = 5.5.0、PHP 7は、私のように本当にこれが機能することを望んでいた人のために..
Matt P

1
注目すべきはstrnatcmp、文字列を比較するためのもので、ここでは問題なく動作するようです。どうやらそれが実装する「自然な順序」には、数値文字列を語彙ではなく数値でソートすることが含まれます。
Mark 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");
}

出力:

first: pork

pork: 5.43
fruit: 3.5
milk: 2.9

6

PHPで指定されたキーの値によって連想配列の並べ替え配列

uasort(http://php.net/uasort)を使用すると、独自に定義した関数で配列をソートできます。あなたの場合、それは簡単です:

$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
この答えは、おそらくコードの説明を提供しなかったために、低品質のレビューキューで判明しました。このコードが質問に回答する場合は、回答にコードを説明するテキストを追加することを検討してください。このようにして、あなたはより多くの賛成票を獲得し、質問者が何か新しいことを学ぶのを助ける可能性がはるかに高くなります。
lmo

1
hmpf。これが最良の答えです。
commonpike

1
-1; cmpここの機能は間違っています。「最初の引数が2番目の引数よりも小さい、等しい、または大きいと見なされる場合、ゼロより小さい、等しい、またはより大きい整数」を返すことになっていますが、代わりにtrueまたはを返しますfalse。それでもなお、それは機能するように思われます-おそらく、現在の実装とusortその友人たちは、「より少ない」と「等しい」のケースを同じように扱っているためですが、将来のPHPバージョンで機能することを期待してはいけません。ソートを安定させようとする場合(つまり、同じ要素を不必要に移動しない場合)、これは機能しなくなります。
Mark Amery

また、キーと値の間の関連付けが保持されるためusortuasortここよりも適切uasortです。これは、連続した数値配列で処理するときに混乱し、予期しないものになります。たとえば、$array呼び出し後の上記のインデックスは、2、0、1のuasort順になります。何らかの理由でそれを望まない限りusort、配列を再インデックスするだけでなく、配列を並べ替えるを使用するほうが快適でしょう。
Mark Amery

5

10万件のレコードでテストされた: 時間(秒単位)(関数マイクロタイムで計算)。 ソートキー位置の一意の値のみ。

@Josh Davisの関数の解決: 費やされた時間:1.5768740177155

鉱山の解決策: 時間:0.094044923782349

解決:

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
ただし、一意のソートキーの要件は、取引ブレーカーのようなものです。キーになる可能性のある一意の並べ替え値がある場合、疑問を投げかけます。まず、これらのキーを使用して配列を作成しないのはなぜですか?OPのシナリオでは、同じ価格の2つのアイテムが不可能であると想像するのは困難です。このことを念頭に置いておくと、このソリューションを使用すると、配列のアイテムが不思議なことに静かに、ソートされた結果セットから消えてしまいます。
Chris Baker、

@クリスベイカー、あなたは正しいです。これは一意の値に対してのみ機能します。しかし、このソリューションは非常に高速に機能するため、速度がそれを作り、使用する理由でした。現時点では、それが実際ではない可能性があります。PHP7.1.xでテストする必要があります。
ネフェリム2017年

4

私はuasortこのように使用します

<?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

この関数は再利用可能です:

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

デフォルトでは文字列値でうまく機能しますが、すべての値が数値の場合は、数値比較関数のコールバックをサブスクライブする必要があります。


これusortarrを呼び出すが、uasort代わりにを呼び出すusort。少し混乱するかもしれません。後者は、質問で示されているような、数値インデックスを持つシーケンシャル配列の場合、おそらく実際に必要なものです。
Mark Amery

2

独自の比較関数を定義してから、usortを使用することもできます。


はい。解決策が見つからない場合は、それを行います。これを達成するためにソートの1つに追加できるいくつかの奇妙なパラメーターがあると確信しています。あなたの考えをありがとう!
マット

2

ここに私がずっと前に見つけて少し掃除した方法があります。これはうまく機能し、オブジェクトを受け入れるようにすばやく変更できます。

/**
 * 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;

}

オブジェクトをソートするメソッドを変更するには、次の行を変更するだけです。

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

メソッドを実行するには、単に

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


このアプローチは機能しますが、Josh Davisの回答(with array_multisort)またはmine(with usort)のどちらよりも簡潔ではなく、その代わりにそれらに勝る利点はないようです。
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

これを試して:

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

こんにちは、stackoverflowへようこそ。回答いただきありがとうございます。このコードは質問に答える可能性がありますが、何が問題を解決したのか、どのように解決したのかについて説明を追加することを検討できますか?これは、将来の読者があなたの答えをよりよく理解し、そこから学ぶのに役立ちます。
Plutian

0

完全な動的関数 連想配列のソートのためにここにジャンプして、この驚くべき関数をhttp://php.net/manual/en/function.sort.phpで見つけました。この関数は非常に動的で、指定したキーを使用して昇順と降順で並べ替えます。

特定のキーで配列をソートする単純な関数。インデックスの関連付けを維持する

<?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;


} 

このコードスニペットは問題を解決する可能性がありますが、説明を含めると、投稿の品質を向上させるのに役立ちます。あなたは将来の読者のための質問に答えていることを覚えておいてください、そしてそれらの人々はあなたのコード提案の理由を知らないかもしれません。また、コードと説明の両方の読みやすさが低下するため、コードを説明コメントで混雑させないようにしてください。
さようならStackExchange 2016

-5

これを試して:

asort($array_to_sort, SORT_NUMERIC);

参考のためにこれを参照してください:http : //php.net/manual/en/function.asort.php

ここでさまざまな並べ替えフラグを参照してください:http : //www.php.net/manual/en/function.sort.php


これは多次元配列では機能しませんが、別の問題を解決するのに役立ちました。おかげで:)
schellmax

4
これは、特定のディクショナリキーでディクショナリのリストをソートするために使用できないため、提起された質問に回答しません。
Mark Amery 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.