I have the following, and then I would like like to resort the array to its original state with the out come being posted to console.log. But I'm getting a bit lost on what direction to take:
$(document).ready(function(){
var cards=new Array(
'clu01',
'clu02',
'clu03',
'clu04',
'clu05',
'clu06',
'clu07',
'clu08',
'clu09',
'clu10',
'clu11',
'clu12',
'clu13',
'dia01',
'dia02',
'dia03',
'dia04',
'dia05',
'dia06',
'dia07',
'dia08',
'dia09',
'dia10',
'dia11',
'dia12',
'dia13',
'hea01',
'hea02',
'hea03',
'hea04',
'hea05',
'hea06',
'hea07',
'hea08',
'hea09',
'hea10',
'hea11',
'hea12',
'hea13',
'spa01',
'spa02',
'spa03',
'spa04',
'spa05',
'spa06',
'spa07',
'spa08',
'spa09',
'spa10',
'spa11',
'spa12',
'spa13'
);
function Shuffle(o) {
for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
};
Shuffle(cards);
console.log(cards);
});
If the original array is sorted alphbetically, you can just do a sort()
cards.sort()
You can also define a custom sorting function:
cards.sort( function(a, b) {
// some logic to compare 2 values to see which goes first
// return 1 if a comes after b
// return -1 if a comes before b
// return 0 if they are the same (this usually means whichever came first will be first)
});
With the time and headache you are going to have in resorting your cards, you might as well have two array, your source "sorted" array, and your shuffled array. When you want to sort your array just use your source array. When you want to shuffle an array you can either reshuffle the "shuffled" array, or make a copy of the source and shuffle that.
The war is already won you can already shuffle cards.
var orig_cards = cards.slice(0);
Shuffle(cards);
console.log(cards);
cards = orig_cards;
console.log(cards);
The way I do things like this is having a separate array of indices, the same length of your array. So, for example for
var cards = ['clu01', 'clu02','clu03'];
you create
var indices = [0, 1, 2];
Now, when you do your shuffle, you compare your original array, but shuffle the indices array. So essentially, you are shuffling the positions in the array but not the elements themselves. That way you keep both separate and can access both states at any time without much overhead.
To access shuffled:
cards[indices[i]];
To access original:
cards[i];
Related
I have a single 1d array storing a series of scores. My end goal is to have the 5 highest scores with brackets around them ( e.g. (score) ) for me to then format and output onto the display. In the case where there are duplicate scores, the first occurrences would be bracketed, up to that 5 top values.
So for example:
[9,8,10,9,6,8,6,5,4,4,3,3,6] would become [(9),(8),(10),(9),6,(8),6,5,4,4,8,3,8]
What I've tried so far is this:
var topvals = scores.sort((a,b) => b-a).slice(0,5);
for(var j=0; j< scores.length; j++){
if(topvals.length==0){
break;
}else if(topvals.includes(scores[j])){
scores[j] = "(" + scores[j] + ")";
topvals.splice(topvals.indexOf(scores[j]),1);
}
}
With the idea that topvals is an array containing the top 5 values, and I then loop through scores looking for those values, removing them each time.
What this results in is the first 5 values of scores having brackets around them.
I'm happy to go a completely different route with this, or just fix what I've done so far. Thanks in advance.
sort with indexes attached. Use index positions to change to desired format. O(N log N) for the sort.
scores = [9,8,10,9,6,8,6,5,4,4,3,3,6]
scores.map((n,i)=>({n,i})) // each object as n: number, i: index
.sort((a,b)=>a.n-b.n).slice(-5) // sort, slice top 5
.forEach(({i})=>scores[i]=`(${scores[i]})`) // add parens by indexes
console.log(scores)
If you have very, very large data sets and need something closer to O(N), you'll want to implement a pivot selecting algorithm. Just sorting is simpler.
The call to sort() sorts scores in place, which means it changes the scores array. So that is why you need to clone it first, then sort, then slice. Also you probably want to eliminate duplicates from your scores. I linked a stack overflow answer that describes how to do that. Since filter does not filter scores in place, but rather returns a new array, you don't need to explicitly call slice(0) to clone scores.
var scores = [9,8,10,9,6,8,6,5,4,4,3,3,6];
// Function from linked SO answer.
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
// Filter unique scores into copy of array, then slice the top 5.
var topvals = scores.filter(onlyUnique).sort((a,b) => b-a).slice(0,5);
for (var j=0; j< scores.length; j++) {
if( topvals.length==0) {
break;
} else if(topvals.includes(scores[j])) {
scores[j] = "(" + scores[j] + ")";
topvals.splice(topvals.indexOf(scores[j]),1);
}
}
Get all unique values in a JavaScript array (remove duplicates)
function question1(){
if(sessionStorage.quizCounter == 0){
var qa0 = qs[Math.floor(Math.random() * 19)];
document.getElementById('questions_container').innerHTML = qa0[0]
for (let i = 1; i <= 3; i++) {
document.getElementById('answers_container').innerHTML += qa0[Math.floor((Math.random() * 2))+1];
}
}
}
That's my function, it's supposed to place the first item in the array in a <div id="questions_container"> which works, but the second part - the for loop - doesn't work.
The for loop is supposed to paste the last 3 items randomly in <div id="answers_container">. And I don't know how to do that beyond the idea. As it stands now it prints duplicates, which I don't want.
qa0[0] is always the question. qa0[1, 2, 3] is always the answers. qa0[] contains always 4 items. I need the first item to always be on qa0[0].
I figured to answer this question in the easiest possible way.
If you dont need to worry about the positions, you can just randomize the data and then remove the N items off the end.
function shuffle(array) {
array.sort(() => Math.random() - 0.5);
}
This will shuffle your array, essentially randomizing it. You can then pop the items off the end
function randomitems(arr, returnedCount){
function shuffle(array) {
array.sort(() => Math.random() - 0.5);
}
const clonedData = arr.slice()
shuffle(clonedData)
if ( returnedCount === undefined) return clonedData;
return clonedData.slice(-1 * returnedCount)
}
This will take up some space because I dont want to change the original object.
Smooth, and Quick. No super complex code.
It will return a random list, and it will return only duplicates IF there are duplicates in the base arr. Otherwise, it will maintain uniqueness of array items.
This will return an array of items, you can then loop that list or do whatever you specifically need with the desired results.
If you want to get super Computer Science-y, I can also give a hyper optimized random set, but that wouldn't likely see much in terms of returns until your dataset started to get big.
This would be used in the following case for you:
// This will link all the questions and possible answers together.
qa0 = [question, ans1, ans2, ans3, ans4].
// The next step is that you need to shuffle the answers.
tmpVar = [].concat(qa0[0], randomizeItems(qa0.slice(1))); // no number will just return the shuffled set.
qa0 = tmpVar; // You could likely just not even use this tmpVar and just do assignment if you really wanted to.
That way, qa0, will have the question be the 0 index, and all items after are shuffled. So it would then look like: qa0 = [ question, ans3, ans1, ans2, ans4];
I will try to be specific as possible as I can't find anything on this through the Google gods.
I have a list of 10 movies. I would like to display the movies in pairs. The user picks their favorite of the two. The next pair is displayed. The user picks their favorite of those two, so on and so on until I can faithfully output the list in their order of preference from 1-10.
I'm doing this in Javascript, but even just a way to do it that is language agnostic would be great. No need to worry about syntax or UI stuff.
With three movies it's pretty easy (initial order of movies and order of pairs shouldn't matter):
1.sw
2.esb
3.rotj
example 1
1vs2: winner = 2
2vs3: winner = 2
1vs3: winner = 1
Sorted List: 2,1,3
example 2
1vs3: winner = 1
1vs2: winner = 2
2vs3: winner = 2
Sorted List: 2,1,3
First time posting so if I need to be more specific, need to have exact syntax, etc., please don't hesitate to let me know.
The minimum number of comparisons required to sort 10 items is 22. (See https://oeis.org/A036604). Do you really think your users will suffer through 22 "which movie do you like better?" questions? And do you honestly believe that the result will be useful? You'll have many cases where a user will say that he liked movie A better than B, and B better than C, but he liked movie C better than he liked movie A. So now you have the problem that:
A > B
B > C
C > A
And there's no reasonable way to resolve that conflict.
In short, your user interface is flawed. You can try to build it, but your users won't like it and your results will not be reliable.
A better interface would be to list the 10 movies and allow the users to arrange them in order of preference. Or let the user rate the movies on a scale from 1 to 5. But expecting users to answer 22 questions and get a complete ordering is a fool's errand.
The basic problem is easy. We have a ton of sorting algorithms that will work with O(n log(n)) comparisons. For instance mergesort:
// Split the array into halves and merge them recursively
function mergeSort (arr) {
if (arr.length === 1) {
// return once we hit an array with a single item
return arr
}
const middle = Math.floor(arr.length / 2) // get the middle item of the array rounded down
const left = arr.slice(0, middle) // items on the left side
const right = arr.slice(middle) // items on the right side
return merge(
mergeSort(left),
mergeSort(right)
)
}
// compare the arrays item by item and return the concatenated result
function merge (left, right) {
let result = []
let indexLeft = 0
let indexRight = 0
while (indexLeft < left.length && indexRight < right.length) {
if (left[indexLeft] < right[indexRight]) {
result.push(left[indexLeft])
indexLeft++
} else {
result.push(right[indexRight])
indexRight++
}
}
return result.concat(left.slice(indexLeft)).concat(right.slice(indexRight))
}
So in principle you need to simply replace left[indexLeft] < right[indexRight] with an arbitrary comparison function that asks the user, gets an answer, and then continues.
Now there is a catch. The catch is that you need to make this code asynchronous. When you go to ask the user, you need to ask the user, then return to inside your code. If you're using node at the console, then you can do this with async/await. If you are not, then you'll need to figure out how to do it with promises. Modifying mergeSort is easy, just make the end into:
return Promise.all([mergeSort(left), mergeSort(right)]
).then(function (values) {return merge(values[0], values[1])});
The trick is in turning the loop inside of merge into a function that takes the current state of your iteration, and returns a promise that asks the question, then either returns the final sorted array, or returns a promise that handles the next iteration.
Since this looks like homework, whose whole purpose is to make you face that mental challenge, I'll leave my answer off here. Fair warning, I gave you a hint about how to do it, but if you're just learning about async code your head is SUPPOSED to spin as you figure it out.
To determine all possible combinations from your array try the following loop. Assuming order does not matter and we do not want repeats:
var arr = [1,2,3,4,5,6,7,8,9,10]
var arr_count = arr.length
var combinations_array = []
for (i = 0; i < arr_count; i++) {
var combinations = arr_count - (i+1)
for (y = 0; y < combinations; y++) {
combination = [arr[i],arr[(combinations - y)]];
combinations_array.push(combination);
}
}
In your example I'd pass Movie ID's into arr then iterate through the combinations_array to determine which combination of movies should be displayed next.
To produce a list of pairs, I would use a nested loop like this:
var items = [1,2,3,4,5,6,7,8,9,10],
result = [],
x = 0,
y = 0;
for (x = items.length; x--;)
{
for(y = x; y--;)
{
result.push({ a: items[x], b: items[y] });
}
}
console.debug(result);
The second loop is initialised from the outer loops incrementor so that you don't end up with duplicates.
Once you have the pairs, you should be able to build the ui from that.
Hope it helps!
I have an array, and need to pick three random values from that array. These will the be put in a new array and I will be able to see the new array on my website. I also have to make sure that no value gets picked twice.
This is what I have so far:
var student = ["Hans","Ole","Nils","Olav","Per","Knut","Line","Pia"];
var velg = student[Math.floor(Math.random() * student.length)];
I'm thinking I should add an id to my HTML, so the new array will show on my website, but I'm not sure about the rest.
First sort it randomly and then get first three:
student
.sort(function(){
return Math.random() - 0.5;
})
.slice(0,3)
Since Math.random() returns random value between 0 and 1, while sort expects values to be positive or negative to determine order we, we need to subtract 0.5 to make those negatives possible.
You could try something like this in a loop
var students = ["Hans","Ole","Nils","Olav","Per","Knut","Line","Pia"];
var randomStudents = [];
for(var i = 0; i < 3; i++) {
var velg = student[Math.floor(Math.random() * students.length)];
randomStudents.push(velg);
}
Note that this can add duplicate students to the array. You should check if student is already in the array and try again.
Keyword for that would be recursion.
https://www.codecademy.com/courses/javascript-lesson-205/0/1
there are many questions/answers dealing with this topic. None match my specific case. Hopefully someone can help:
I have an array of indexes such as:
var indexes = [24, 48, 32, 7, 11];
And an array of objects that look similar to this:
var items = [{
name : "whatever",
selected : false,
loading : true,
progress : 55,
complete : false
},
{
name : "whatever 2",
selected : false,
loading : false,
progress : 100,
complete : true
}];
Each integer within the indexes array corresponds to the actual index of an object within the items array.
Lastly I have a variable which defines the new insert position within the items array:
var insertindex = ??
What I would like to do is to take all objects in the items array that have the indexes stored in the indexes array, remove them, then finally place them back, all next to each other at a specified index defined by the variable insertindex.
I have been trying to use splice() by copying the objects at each index to a temporary array, then removing them from the original array, then finally looping through this new temporary array and putting them back into the original items array at the new positions, but seems to be hitting a mental brick wall and cannot get it to work correctly.
To summarize, I simply want to take all objects from the items array that match an index defined in the indexes array, put them together and reinsert them at a predefined index, back into the items array.
To help with conceptual visualization. If you think of the app as a javascript file manager, allowing the reordering of multiple file selections which do not have to be adjacent. The indexes array defining the current selection and the items array defining the list of files. And finally the rearoderindex defines the new insert position that all selected files should move to.
EDIT: As was rightly suggested here is the code I am playing with right now:
function reorder(items, indexes, insertindex){
var offset = 0;
var itemscopy = items.slice(0); //make shallow copy of original array
var temparray = new Array(); // create temporary array to hold pulled out objects
//loop through selected indexes and copy each into temp array
for(var i=0, len=indexes.length; i<len; i++){
array[i] = itemscopy[self.cache.selecteditems[i]];
}
//remove all selected items from items array
for(var i=0, len=indexes.length; i<len; i++){
items.splice(indexes[i], 1);
}
//finally loop through new temp array and insert the items back into the items array at the specified index, increasing the index each iteration using the offset variable.
for(var i=0, len=temparray.length; i<len; i++){
items.splice((insertindex+offset), 0, array[i]);
offset++;
}
}
I'm aware this is pretty horrible and that looping three times should not be necessary. But I've been trying lots of different methods, some working when reordering in one direction, some in the other an mostly, not at all. I figured I would look to optimize the function later, once I have it working with accuracy.
I'm certain I must be doing something extremely stupid or completely overlooking something, but for the life of me I can't work out what right now.
If you don't care about order of indexes array, I'd suggest another short solution:
items.splice.apply(items, [insertIndex, 0].concat(indexes.sort(function(a, b) {
return a - b;
})).map(function(i, p) {
return p > 1 ? items.splice(i - p + 2, 1).pop() : i;
}));
DEMO: http://jsfiddle.net/T83fB/
To make it short I used Array.map() method, which is not supported by old IE browsers. However it is always easy to use a shim from MDN.
You can use the .splice() function to add elements to an array, as well as removing items from it. The general principle is:
Sort indexes into ascending numeric order
Iterate over indexes, removing the element at that index (adjusting for the number of removed items) and storing it in a removedItems array
Add the removedItems array back in at the required index
The code to do that would look something like this:
var removedItems = [];
// sort indexes
indexes.sort(function(a, b) {
if(a < b) return -1;
else if(b < a) return 1;
return 0;
});
for(var i = 0; i < indexes.length; i++) {
var index = indexes[i];
removedItems.push(items.splice(index - removedItems.length, 1));
}
var insertIndex = 1;
items.splice.apply(items, [insertIndex, 0].concat(removedItems));
Take a look at this jsFiddle demo.