I am building a music site in Angular where the user can shuffle the songs on click of a shuffle button. I can get the shuffle to work with the below code:
var shuffleArray = function(array) {
var m = array.length, t, i;
// While there remain elements to shuffle
while (m) {
// Pick a remaining element…
i = Math.floor(Math.random() * m--);
// And swap it with the current element.
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
}
$scope.shuffleThis = function(tracks){
shuffleArray(tracks);
}
What I can't figure out is how to allow the user to revert to the original order without refreshing the page or calling for the original JSON response.
UPDATE:
So I went with the following code, essentially cloning the array in order to use it later, but I am encountering an issue. When I go to unshuffle the array (bind the $scope.tracks to the $scope.unshuffled) it is setting the $scope.tracks of the parent controller. The structure I currently have is: mainCtrl is set at the html tag and this is where the shuffle/unshuffle functions live. Then each page url has a controller. To give you some context, here is the site I am building...
www.beta.audiblecoffee
As you can see, if you start to play a song, the bottom player control appears. when you click the shuffle button once (bottom right), the songs shuffle. Then you click the shuffle button again, nothing happens... now if you select a top nav item (New, Popular, Shuffled, etc), the original ordered array will briefly appear, then the new $scope.tracks from that particular page's controller will overwrite the $scope.tracks from the unshuffle function. But if you click another top nav item, the same will happen. SO I guess my question is how can I set the unshuffled array to the child controller $scope.tracks?
var shuffleArray = function(array) {
var m = array.length, t, i;
// While there remain elements to shuffle
while (m) {
// Pick a remaining element…
i = Math.floor(Math.random() * m--);
// And swap it with the current element.
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
}
$scope.shuffleThis = function(tracks) {
$scope.isShuffled = true;
$scope.unshuffled = tracks.slice(0);
shuffleArray(tracks);
console.log(player.tracks);
}
$scope.unshuffleThis = function() {
$scope.isShuffled = false;
player.tracks = $scope.unshuffled;
$scope.tracks = $scope.unshuffled;
}
I am building a music site in Angular where the user can shuffle the songs on click of a shuffle button. I can get the shuffle to work with the below code:
var shuffleArray = function(array) {
var m = array.length, t, i;
// While there remain elements to shuffle
while (m) {
// Pick a remaining element…
i = Math.floor(Math.random() * m--);
// And swap it with the current element.
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
}
$scope.shuffleThis = function(tracks){
shuffleArray(tracks);
}
What I can't figure out is how to allow the user to revert to the original order without refreshing the page or calling for the original JSON response.
UPDATE:
So I went with the following code, essentially cloning the array in order to use it later, but I am encountering an issue. When I go to unshuffle the array (bind the $scope.tracks to the $scope.unshuffled) it is setting the $scope.tracks of the parent controller. The structure I currently have is: mainCtrl is set at the html tag and this is where the shuffle/unshuffle functions live. Then each page url has a controller. To give you some context, here is the site I am building...
www.beta.audiblecoffee.
As you can see, if you start to play a song, the bottom player control appears. when you click the shuffle button once (bottom right), the songs shuffle. Then you click the shuffle button again, nothing happens... now if you select a top nav item (New, Popular, Shuffled, etc), the original ordered array will briefly appear, then the new $scope.tracks from that particular page's controller will overwrite the $scope.tracks from the unshuffle function. But if you click another top nav item, the same will happen. SO I guess my question is how can I set the unshuffled array to the child controller $scope.tracks?
var shuffleArray = function(array) {
var m = array.length, t, i;
// While there remain elements to shuffle
while (m) {
// Pick a remaining element…
i = Math.floor(Math.random() * m--);
// And swap it with the current element.
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
}
$scope.shuffleThis = function(tracks) {
$scope.isShuffled = true;
$scope.unshuffled = tracks.slice(0);
shuffleArray(tracks);
console.log(player.tracks);
}
$scope.unshuffleThis = function() {
$scope.isShuffled = false;
player.tracks = $scope.unshuffled;
$scope.tracks = $scope.unshuffled;
}
Share
Improve this question
edited Feb 24, 2015 at 23:29
jeanhules
asked Feb 24, 2015 at 16:07
jeanhulesjeanhules
2355 silver badges16 bronze badges
1
- 7 Save a copy of the original data and shuffle a clone of it. – Vinay K Commented Feb 24, 2015 at 16:10
5 Answers
Reset to default 3The idea here is to use an intermediate reference and never actually modify the original playlist
var originalArray = [1,2,3];
var playlist = originalArray;
function shufflePlaylist() {
playlist = shuffleArray(originalArray.slice());
}
function restoreOriginalPlaylist() {
playlist = originalArray;
}
As a side note: this is not really an angular thing, you'll just have to bind it to scope afterwards.
You can:
keep a reference to the original array and shuffle a copy each time:
var originalTracks = ... $scope.shuffleThis = function(tracks) { shuffleArray(originalTracks.slice(0)); };
return a copy of the array from the function:
var shuffleArray = function(array) { var m = array.length, t, i; array = array.slice(0); ... };
You could add a property "shuffledIndex" to every item and filter the list based on this index (with angular's filter orderBy, see https://docs.angularjs/api/ng/filter/orderBy).
This prevent you from storing 2 lists : the original and the shuffled one.
var shuffleArray = function(array) {
var m = array.length, t, i;
// While there remain elements to shuffle
while (m) {
// Pick a remaining element…
i = Math.floor(Math.random() * m--);
array[m].shuffledIndex = i;
}
return $filter('orderBy')(array, 'shuffledIndex');
}
Why don't you leave the original array where it is, and gen a random number between 0 and the length of the array - 1?
array = ['test1','test2','test3','test4']
getRandomResult = function(){
var randomIndex = Math.floor(Math.random() * array.length - 1) + 1
var item = array[randomIndex];
array.splice(randomIndex, 1) //remove to have the possibility for the same song twice in a row
return item;
}
this way you don't have to duplicate it and you don't have to reorder it.
Alternatively you could do this
array = ['test1','test2','test3','test4']
indexes = []
for(var i = 0; i < array.length; i++){
indexes.push(i)
}
indexes = shuffleArray(indexes); //your original function
then just pull take the first index out and grab the song at that index.
This does take a little more memory, but at least your not duplicating the full original array.
$scope.shuffleThis = function(tracks) {
$scope.unshuffled = tracks.slice(0);
shuffleArray(tracks);
}
Or, if you don't want to save the original on your $scope:
var unshuffled;
$scope.shuffleThis = function(tracks) {
unshuffled = tracks.slice(0);
shuffleArray(tracks);
}
Actually, I think in general it's really better to return a clone rather than modify the original; the latter is really more of a side-effect and would require careful documentation in practical application:
function shuffle(array) {
var m = array.length, t, i;
array = array.slice(0);
// While there remain elements to shuffle
while (m) {
// Pick a remaining element…
i = Math.floor(Math.random() * m--);
// And swap it with the current element.
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
}
I'm also not really sure why you don't just set $scope.shuffleThis = shuffleArray
, but that's up to you.