AngularJS で ng-repeat を使ってデータをソートする

下記のようなデータがある。

var $scope.items = { { id:1, data1:'abc', data2: 'foo' },
                     { id:2, data1:'def', data2: 'foo' },
                     { id:3, data1:'ghi', data2: 'foo' } }

AngularJS の ng-repeat 記法を使ってデータを表形式で表示したい場合、下記のように書く。

  ...
  <table>
    <tr ng-repeat="item in $scope.items">
      <td><span ng-bind="item.id"></span></td>
      <td><span ng-bind="item.data1"></span></td>
      <td><span ng-bind="item.data2"></span></td>
    </tr>
  </table>
  ...

表を列でソートしたい場合は JavaScript をちょっと書くだけでできてしまう。昇順、降順もお手の物。

  var sortByColumn = function(columnName) {
    $scope.predicate = columnName;    //ソートする列名(データの項目名とそろえる)
    $scope.reverse = !$scope.reverse; //昇順降順切り替え
  }
  ...
  <table>
    <tr>
      <th><a ng-click="$scope.sortByColumn('id')">id</a></th>
      <th><a ng-click="$scope.sortByColumn('data1')">id</a></th>
      <th><a ng-click="$scope.sortByColumn('data2')">id</a></th>
    </tr>
    <tr ng-repeat="item in $scope.items | orderBy:$scope.predicate:$scope.reverse">
      <td><span ng-bind="item.id"></span></td>
      <td><span ng-bind="item.data1"></span></td>
      <td><span ng-bind="item.data2"></span></td>
    </tr>
  </table>
  ...

この状態で data2 を昇順降順でソートすると、id の昇順降順と同じ結果になる。(data2 はすべて同じ値で構成されているため)
今日はここ頭を悩ませる問題が起きた。


表示されたデータに対して操作を行うと、AngularJS は操作されたデータに一意のハッシュを自動で割り当てるらしい。こんな感じ。

var $scope.items = { { id:1, data1:'abc', data2: 'foo' },
                     { id:2, data1:'def', data2: 'foo', $$hashkey: 'Object:11' },
                     { id:3, data1:'ghi', data2: 'foo' } }

この状態で data2 のソートを行うと、$$hashkey を持っているデータでソート→持っていないデータでソートがかかるため。データの並び順がおかしくなる。
具体的には id1 → id2 → id3 を期待しているのに id2 → id1 → id3 の順番になる。
この現象が起きるのは値がすべて同じデータをソートしようとしたときのみ。ユニークな値を持つ列でソートをかけると発生しない。
$$hashkey がどのタイミングで追加されるのか見当もつかないので、どうやって解決したもんかと思ったが、最終的には ng-repeat を以下のようにすることで解決した。

  <tr ng-repeat="item in $scope.items | orderBy:[$scope.predicate, $index]:$scope.reverse">

orderBy はソートする列を複数指定できるので、「指定した列」と「データの現在位置」の2つを指定することで $$hashkey を気にせずに期待する結果が得られるソートができるようになった。
AngularJS は短いコードで書けるってのはいいんだけど、理解していることが前提だから難しい。