2

Is there a way to sort a 1:1 object map in angular.js using filters (or any other method) within a ng-repeat block?

I have:

obj:{
     a:3,
     c:5,
     d:1,
     g:15
}

Essentially I'd like to be able to do something like this:

<div ng-repeat="item in obj | orderBy:'value'"> {{ item.value }}</div>

I would like to be able to sort that data representation either by key or value. I know that angular OrderBy will only sort arrays so it won't sort that object. However, if I could turn that object into an array of objects with key and value as properties (preferably using a filter called 'toArray' ):

var arr = [
  { 
     key:'a',
     value:'3'
  },
  {
    key:'c',
    value:5
  },
  ....
]

then I could use something like this:

<div ng-repeat="item in obj | toArray | orderBy:'value'"> {{ item.value }}</div>

However, any attempt to write such a filter has produced a '10 $digest() iterations reached. Aborting!' error as it updates the new array of objects on every iteration.

What is the best (or any) solution to sorting a 1:1 object map without having to restructure the data in the controller?

1
  • Please provide the code to your toArray filter, so we can go from there. A stab in the dark: does your toArray handle cycles properly? Some AngularJS objects have cycles. Commented Jun 25, 2014 at 16:02

2 Answers 2

3

Try angular-underscore which introduces underscore functions to angular.js. Then you can write code like:

<div ng-repeat="item in values(obj) | orderBy:identity"> {{ item }}</div>

to get only values; or

<div ng-repeat="item in pairs(obj) | orderBy:last"> Key: {{ item[0] }}; Value: {{item[1]}}</div>

to get both keys and values.

It's really easy. Plunker here: http://plnkr.co/edit/Zb4XpaQUXZ8nZJqR493b?p=preview

Sign up to request clarification or add additional context in comments.

2 Comments

this looks promising, I see it orders the list by value from lowest to highest value. how would you reverse that order?...from highest value to lowest value
@user1394625 just add the :reverse parameter like this: <div ng-repeat="item in values(obj) | orderBy:identity:true"> {{ item }}</div>. You can refer to AngularJS doc orderBy for more information.
0

Have you thought about just creating the array initially in your controller like so?

$scope.items = [];
for(var key in obj) {
    if(obj.hasOwnProperty(key)) {
        $scope.items.push({ key: key, value: obj[key] });
    }
}

Then iterate over items?

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

At least then you are only populating the array one time. This could be an issue if the initial object comes from a request or something, but I guess you could quite easily populate in the callback of said request.

2 Comments

At the moment that is the only solution I have...to do it in the controller. I was hoping there was a way to do it in the view. It's not just one object that is structured that way so I'd have to run each object through a function in the controller. Not a deal breaker but it looks cleaner just piping through a filter in the view.
you could likely do it as a filter or directive somehow, but it wouldn't really be great for performance. I imagine it'd keep evaluating it every time the watcher is triggered, so you'd be generating that array every time. Maybe just make a service you inject into your controller to do this? (like Utils.somehelper)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.