Using AngularJS how could I randomize the order of a collection?

12,627

Solution 1

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.

Solution 2

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

Share:
12,627
rmontgomery429
Author by

rmontgomery429

Updated on June 29, 2022

Comments

  • rmontgomery429
    rmontgomery429 almost 2 years

    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.

    • Dogbert
      Dogbert almost 11 years
      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.
    • Blazemonger
      Blazemonger about 10 years
      @Dogbert has the right of it. In current versions of AngularJS, the accepted answer below generates 'infdig' errors in the console, because sorting is repeated until the same order is produced twice. See this question.
  • SJuan76
    SJuan76 almost 11 years
    But the value will not be consistent (it means that you may find that sometimes a<b and b<a, in the same ordering). Are you sure this would have no side effects?
  • SJuan76
    SJuan76 almost 11 years
    Ready my original comment. If each comparation is random, you could end with a<b, b<c, a>c, 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.
  • Dan
    Dan almost 11 years
    Glad it worked out for you. You shouldn't need to use parens to invoke it, check the fiddle from @owenmead below.
  • rmontgomery429
    rmontgomery429 almost 11 years
    @sh0ber You are correct. The correct way to execute the filter is with only the function name without parens. Thanks.
  • Jossef Harush Kadouri
    Jossef Harush Kadouri over 9 years
    FYI - this is causing Error: [$rootScope:infdig]
  • David
    David almost 9 years
    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")
  • cchapman
    cchapman about 8 years
    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.
  • Prateek Choudhury
    Prateek Choudhury over 7 years
    Also a point to be noted that this does not work when you use track by $index. Thanks for the solution.
  • phazei
    phazei about 7 years
    This is wrong and shouldn't be used as explained in the comments here stackoverflow.com/a/18650169/65985
  • Dan
    Dan about 4 years
    @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.