AngularJS Filter for Ordering Objects (Associative Arrays or Hashes) with ngRepeat


I ran into an issue today using the ngRepeat directive in AngularJS. ngRepeat let’s you iterate over a collection (array or object) and repeat a snippet of code for each of the items. Let’s look at a some examples first, before I get into my situation.

Iterating an Array Strings

Let’s assume we have an array of items set in our controller’s scope.

$scope.items = ["red", "green", "blue"];

We can loop through these items using ngRepeat in the controller’s template.

  <li ng-repeat="item in items">{{ item }}</li>

Iterating an Array of Objects

Ok, now let’s get slightly more advanced. Let’s say we have an array of objects with a color field.

$scope.items = [
  {color: "red"},
  {color: "green"}, 
  {color: "blue"}

Again, we can use ngRepeat to loop through them. This time we’ll incorporate a built-in filter to sort by that color field.

  <li ng-repeat="item in items | orderBy:'color'">{{ item.color }}</li>

Easy enough. The orderBy filter uses the color property to sort the objects before iterating through them.

Iterating an Object of Objects (Acting as an Associative Array or Hash)

Finally, we’ll look at the situation I ran into. Instead of $score.items being an array, it’s going to be an object used as an associative array (or hash). So we have something like this:

$scope.items = {};
$scope.items['color-1'] = {color: "red"};
$scope.items['color-2'] = {color: "green"};
$scope.items['color-3'] = {color: "blue"};

This may look the same as the example above, but it’s not. $score.items is no longer an array of objects. It is an object itself (acting like an associative array or hash) with the properties color-1, color-2, and color-3. We need to iterate through those properties. ngRepeat does have the ability to do this.

  <li ng-repeat="(key, item) in items | orderBy:'color'">{{ item.color }}</li>

However, the built-in orderBy filter will no longer work when iterating an object. It’s ignored due to the way that object fields are stored. This was the issue I ran into. And here is my solution… a custom filter:

yourApp.filter('orderObjectBy', function() {
  return function(items, field, reverse) {
    var filtered = [];
    angular.forEach(items, function(item) {
    filtered.sort(function (a, b) {
      return (a[field] > b[field]);
    if(reverse) filtered.reverse();
    return filtered;

This filter converts the object into a standard array and sorts it by the field you specify. You can use the orderObjectBy filter exactly like orderBy, including a boolean value after the field name to specify whether the order should be reversed. In other words, false is ascending, true is descending.

  <li ng-repeat="item in items | orderObjectBy:'color':true">{{ item.color }}</li>

Note: Because this filter converts the items object to an array, you will no longer have access to the key of the “associative array”, as you do with the (key, item) in items snippet. I did not need the key in my situation, so this was fine.

  • Justin Noel

    Thanks for this. I ran into the same problem. Coming from a PHP background, JavaScripts differences with “associate arrays” have been killing me. With my AngularJS app, I gave up with the associative array ordering properly.

    Now you’ve shown I should have dug a bit deeper.

  • Jacob Evan Shreve

    Thanks! I was getting very frustrated with orderBy not working on my object of objects, and not getting any error feedback.

  • Adam Brooks

    Much appreciated Justin!

  • park seung hyun

    i add that “sort by key”

    • Thomas Waldecker

      Thanks for sharing!

  • Michael Taranto

    This was a great solution. My only issue I had was that it worked in Chrome but failed in my unit tests which were being run in PhantomJS. I narrowed it down to the compare function that gets passed to sort returning a boolean instead of 1, 0, or -1.

    So rather than
    filtered.sort(function (a, b) {
    return (a[field] > b[field]);

    I changed it to
    filtered.sort(function (a, b) {
    return (a[field] > b[field]) ? 1 : ((a[field] < b[field]) ? -1 : 0);

    Now it works everywhere and passes in my unit tests too

    • Justin Klemm

      Michael, thanks for the heads up on this! I’ll do some testing and look at changing the code in the post.

  • Nir Melamoud

    Thanks, you are the best!

  • thallisphp

    Thanks, you saved my night =D

  • Stefan Kleineikenscheidt

    Thanks Justin. Any idea, how this could make it into AngularJS?

  • Logan

    Exactly what I was looking for while trying to sort a collection. I needed the key, but fortunately, I had this available as an attribute in the collection, so I just used {{item[0].key}} in my view. Works great! Thank you!

    • Justin Klemm

      Logan, glad it was useful! Note that if you end up needing the key in a collection where it’s not available, a developer commented further down with a solution that adds it to each item:

      • Logan

        Good to know!

      • Alexander Nyquist

        As I see it, the more angular-way would be to name it $key instead of just key.

  • Bartlomiej Skwira

    Here is a CoffeeScript version I used

    yourApp.filter “orderObjectBy”, ->

    (items, field, reverse) ->

    filtered = []

    angular.forEach items, (item) ->

    filtered.push item

    filtered.sort (a, b) ->

    a[field] > b[field]

    filtered.reverse() if reverse


    later also user the ‘key’ version modified to id (thats the key in my collection)

    myApp.filter “orderObjectBy”, ->

    (items, field, reverse) ->

    filtered = []

    angular.forEach items, (item, key) ->

    item['id'] = key

    filtered.push item

    filtered.sort (a, b) ->

    a[field] – b[field]

    filtered.reverse() if reverse


  • Plasmed

    Thanks a lot!

  • Woody Hayday

    This is Killer.

    Useful :)

    Thanks Justin!

  • Pachito Marco Calabrese


  • Claus Colloseus

    Another, even shorter solution would be to convert the object into an array before you start to repeat:
    ng-repeat=”item in itemsToArray(items) | orderBy: ‘color’”


    $scope.itemsToArray = function (items) {
    var array = [];
    angular.forEach($scope.words, function(item) {
    return array;