10

How would you order a list of items in AngularJS in random order? I was thinking that the built-in orderBy filter would work but I'm not sure how without adding some additional data to the model. Something like would be great.

item in items | orderBy:random

My next thought was to create a custom filter but I'd prefer to avoid that if there is something better already available.

rmontgomery429
  • 14,660
  • 17
  • 61
  • 66
  • 4
    Not an expert in Angular, but the filter might get evaluated anytime, and would just keep shuffling the items all the time. You might want to add a "random" property to items when they're loaded, and order by that property. – Dogbert Jun 14 '13 at 06:00
  • @Dogbert has the right of it. In current versions of AngularJS, the [accepted answer below](http://stackoverflow.com/a/17102267/901048) generates 'infdig' errors in the console, because sorting is repeated until the same order is produced twice. See [this question](http://stackoverflow.com/questions/21586369/random-orderby-in-angularjs-1-2-returns-infdig-errors). – Blazemonger Feb 05 '14 at 20:22

2 Answers2

17

EDIT Warning!: These results are skewed, don't use this. This answer is only left as a warning until further editing.

Explanation: There should be an equal chance of any item being in the first position, but the actual percent chance after 10,000 iterations of, for example, 6 items, ends up being

1: ~28%, 2: ~10%, 3: ~14%, 4: ~20%, 5: ~12%, 6: ~15%

https://jsfiddle.net/sh0ber/km9cqvpf/

orderBy can take a function parameter, just like array.sort so you can use your HTML above and define a function random on the scope like:

$scope.random = function(){
    return 0.5 - Math.random();
};

This will return a random value sometimes negative, sometimes positive, sometimes 0, which will randomly sort the array.

Community
  • 1
  • 1
Dan
  • 59,490
  • 13
  • 101
  • 110
  • But the value will not be consistent (it means that you may find that sometimes `a – SJuan76 Jun 14 '13 at 06:56
  • Ready my original comment. If each comparation is random, you could end with `ac`, and you should ensure that the sorting algorithm can handle that (in fact, it would not match the definition of `order`). Dogbert's comment outlines a method for random ordering where the order is random AND consistent across a run. – SJuan76 Jun 14 '13 at 12:23
  • Glad it worked out for you. You shouldn't need to use parens to invoke it, check the fiddle from @owenmead below. – Dan Jun 14 '13 at 20:54
  • @sh0ber You are correct. The correct way to execute the filter is with only the function name without parens. Thanks. – rmontgomery429 Jun 17 '13 at 13:44
  • Also a point to be noted that this does not work when you use track by $index. Thanks for the solution. – Prateek Choudhury Dec 22 '16 at 04:57
  • This is wrong and shouldn't be used as explained in the comments here http://stackoverflow.com/a/18650169/65985 – phazei Feb 07 '17 at 00:28
  • @phazei - Thanks. It's true the results are skewed. I will edit this answer, however, it's not nearly as bad as the top voted comment there claims with no evidence. See [this example](https://jsfiddle.net/sh0ber/km9cqvpf/). – Dan Mar 22 '20 at 12:43
6

Doing a quick fiddle sh0ber method seems to work well: http://jsfiddle.net/owenmead/fa4v8/1/

<div ng-controller="MyCtrl">
  <p ng-repeat="i in list|orderBy:random">{{i}}</p>
</div>

function MyCtrl($scope) {
  $scope.list = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
  $scope.random = function() {
    return 0.5 - Math.random();
  }
}

Angular's orderBy uses JavaScript's sort() on a copy of the list. Looking at another answer, certain browsers are stable in their sort, others are not. Perhaps just test the fiddle in a few browsers and you should be good to go: Array.sort Sorting Stability in Different Browsers

PS. Couldn't comment on sh0ber's answer as I don't have 50 rep

Community
  • 1
  • 1
owenmead
  • 111
  • 1
  • 2
  • 9
    FYI - this is causing `Error: [$rootScope:infdig]` – Jossef Harush Kadouri Sep 23 '14 at 21:07
  • 1
    for `Error: [$rootScope:infdig]` this happens in later versions of angular where the digest cycle executes the random function twice and as the results are inconsistent it retries (thus creating an infinite digest cycle or "infdig") – David Jun 08 '15 at 01:23
  • 1
    A note on @DavidAnderton's point (which was very helpful). I realized I had two number fields that were being randomized which was throwing the error. I added a `track by model.id` and it is now working great. – cchapman Apr 16 '16 at 15:30