【AngularJS】 ng-repeatでデフォルトの逆順にソートしたい

,

主キーを置いて昇順・降順にソートする方法はよく見かけるのですが、配列やオブジェクトを元のインデックスの逆順で表示する方法が見当たらなかったので調べてみました。

ng-repeatのソートはorderByを使用します。

<!DOCTYPE html>
<html ng-app="myApp">
<head>
    <meta charset="utf-8" />
    <title>Calculate</title>
</head>
<body>
    <div ng-controller="mainCtrl">
        <h1>v1.6.9</h1>
        <ul>
            <li ng-repeat='testItem in testArray track by $index'>{{testItem}}</li>
        </ul>
    </div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<script type="text/javascript">
    var myapp = angular.module('myApp',[]); 
    myapp.controller('mainCtrl', function ($scope) {
        $scope.testArray = [
            { No:20, name:'saitou', score:97 },
            { No:2, name:'tanaka', score:80 },
            { No:32, name:'okuyama', score:90 },
            { No:8, name:'ariyoshi', score:92 },
            { No:17, name:'oda', score:87 },
            { No:6, name:'nakagawa', score:80 },
        ];
    });
 </script>
</body>
</html>

特にソートを指定しないままページを表示するとこのようになります↓

・{"No":20,"name":"saitou","score":97}
・{"No":2,"name":"tanaka","score":80}
・{"No":32,"name":"okuyama","score":90}
・{"No":8,"name":"ariyoshi","score":92}
・{"No":17,"name":"oda","score":87}
・{"No":6,"name":"nakagawa","score":80}

そして今回求めている表示結果がこちら↓(降順)

・{"No":6,"name":"nakagawa","score":80}
・{"No":17,"name":"oda","score":87}
・{"No":8,"name":"ariyoshi","score":92}
・{"No":32,"name":"okuyama","score":90}
・{"No":2,"name":"tanaka","score":80}
・{"No":20,"name":"saitou","score":97}

 

orderByの初期値はどういう順番?

そもそもですが、orderByの使い方について確認してみます。

orderByはプロパティ名を指定すると、それを主キーとしてソートが行われます。
以下はプロパティ”score”を主キーとした場合の書き方です。(上記サンプルコードの11行目)

<li ng-repeat='testItem in testArray|orderBy:"score" track by $index'>{{testItem}}</li>
表示結果
・{"No":2,"name":"tanaka","score":80}
・{"No":6,"name":"nakagawa","score":80}
・{"No":17,"name":"oda","score":87}
・{"No":32,"name":"okuyama","score":90}
・{"No":8,"name":"ariyoshi","score":92}
・{"No":20,"name":"saitou","score":97}

→scoreの昇順(デフォルト)にソートされる

ではプロパティを指定しないとどうなるか。
orderByの初期値の並び順は、型によって異なるようです。

The default comparator
The default, built-in comparator should be sufficient for most usecases. In short, it compares numbers numerically, strings alphabetically (and case-insensitively), for objects falls back to using their index in the original collection, and sorts values of different types by type.
引用元)AngularJS: API: orderBy

訳)数値は数値で、文字列はアルファベット順(大文字と小文字を区別しない)で比較、オブジェクトは元のコレクションのインデックスを使用して戻す。型ごとに異なる型の値をソートする。

実際に試してみます。

HTMLは同じもの使用し、ng-repeatは上記と同じ「’testItem in testArray|orderBy:”” track by $index’」とします。JavaScript内の$scope.testArrayが配列の場合オブジェクトの場合を用意。

配列の場合

See the Pen EQBoyj by BICOxxx (@BICOxxx) on CodePen.

オブジェクトの場合

See the Pen paXpEB by BICOxxx (@BICOxxx) on CodePen.

確かに文字列の配列はアルファベット順、オブジェクトはインデックス順に表示されました。
なので、オブジェクトに限って言えば以下のどの書き方でも「デフォルトの降順でソート」することができます。

<li ng-repeat='testItem in testArray|orderBy:"":true track by $index'>{{testItem}}</li>
<li ng-repeat='testItem in testArray|orderBy:"-" track by $index'>{{testItem}}</li>
<li ng-repeat='testItem in testArray|orderBy:"+":true track by $index'>{{testItem}}</li>

*orderByのプロパティ後の”true”は昇順降順を制御。降順の場合true

ただ、これだと型によっては「デフォルトの降順でソート」にならないのでややこしい。。

ということで、どの型でもデフォルトの降順ソートにする方法を調べました。

デフォルトの降順でソートする方法

1. orderBy:”$index” (オススメ)

<li ng-repeat='testItem in testArray|orderBy:"$index":true track by $index'>{{testItem}}</li>

ng-rpeat内で使える変数$indexをキーにしたやり方。色々検証してて発見した方法です。

2. カスタムフィルター

<li ng-repeat='testItem in testArray|reverse track by $index'>{{testItem}}</li>
var myapp = angular.module('myApp',[]); 
myapp.controller('mainCtrl', function ($scope) {
    $scope.testArray = [
        { No:20, name:'saitou', score:97 },
        { No:2, name:'tanaka', score:80 },
        { No:32, name:'okuyama', score:90 },
        { No:8, name:'ariyoshi', score:92 },
        { No:17, name:'oda', score:87 },
        { No:6, name:'nakagawa', score:80 },
    ];
});
myapp.filter('reverse', function() {
  return function(items) {
    return items.slice().reverse();
  };
});

カスタムフィルターを設定して(ここでは「reverse」)使う方法です。

3. カスタムフィルター(ng-repeat内)

<li ng-repeat='testItem in testArray.slice().reverse() track by $index'>{{testItem}}</li>

前述のカスタムフィルターの処理をng-repeat内に記載した場合がこちら。降順処理だけならこっちの方がシンプルでわかりやすいですね。


上記は最新版v1.6.9であればどれも使えることを確認してます。

参考)javascript – angular ng-repeat in reverse – Stack Overflow

まとめ

初期値の並び順が型によって違うのは盲点でした。
上記3つの中では、orderBy:’$index’を使用するのがシンプル&直感的で良いかと思います。

バージョンによっては動作しない書き方があるようなので、動かなかったら他の書き方を試してみてください!