I'm trying to change the contents of sequential divs with randomly-ordered contents. It's not working so well.
I have about 25 <div> tags that, because of other javascripts regulating touch behaviour, are unable to be put in a randomised order using the usual methods. So instead, I'm trying to have the contents of those <div>s randomised by setting different innerHTML values when the document is loaded.
I have a script that provides random integers as I need, but then I'm having trouble getting it to populate the ordered divs.
Here's what I'm using that isn't working:
ArrayList<Integer> list = new ArrayList<Integer>(25);
for(int i = 0; i < 25; i++)
{
list.add(i);
}
Collections.shuffle(list);
Object[] randomNumbers = (Object[])list.toArray();
function testit() {
for(int i = 0; i < 25; i++)
{
var a = document.getElementById('if'+i);
a.innerHTML = randomNumbers[i];
}
}
What I'd like to see is that this takes all my <div>s which have id's such as "if1" "if2" and so one and then set the content of that <div> with the randomised numbers.
I know document.getElementById('if'+i) is surely the wrong way to get ID "if1" then "if2", but I'm not really sure how else it can be done.
Basically I just need a way to populate 25 absolutely-positions <div>s with 25 chunks of HTML content but in a random order. I'd think that could be done by pairing sequential numbers to randomly ordered numbers and tying those into <div> id's. Normally I'd just do this with PHP and be done already but I need it to work as an offline mobile webapp so I'm trying to do it with JS and CSS. I'm definitely open to other approaches.
edit:
If the code above is Java and not Javascript, that's the problem. Then I guess I'm looking for alternative ways, in Javascript, to produce random, non-repeating integers and link those to sequential integers, all between 1 and 25
Here is a sample implementation using javascript:
function shuffle(arr){
//simple shuffle algorithm - you can also use other
//we will get simple base index as basis for swapping elements
//we will swap the array elements arr.length times
var index = Math.floor(Math.random() * arr.length);
for(var i=1;i<arr.length; i++) {
var t = arr[i];
arr[i] = arr[index];
arr[index] = t;
}
return arr;
}
//create an array
var numbers =[];
for(var i=0;i<10;i++)
numbers.push(i);
//call the shuffle function
//call this many times to reshuffle elements
numbers = shuffle(numbers);
//document.write(numbers.join(','));
//test
for(var i=0;i<numbers.length;i++)
{
var a = document.getElementById('if'+i);
a.innerHTML = numbers[i];
}
Related
I have a quiz that has 100+ questions. When the questions load, only 50 of the questions show. The rest are hidden. I am using the following code:
CSS
.question{
display:none;
}
HTML
<div class="question">
<!-- Code for Each Question -->
</div>
<!-- Repeat 'question div' for every question. -->
JS
var divs = $("div.question").get().sort(function(){
return Math.round(Math.random())-0.5;
}).slice(0,50)
$(divs).show();
The above code works great, however, instead of just showing 50 questions I would like to show 50 questions in a random order. How would I modify my code to not only show only 50 questions but show them in a random order?
I should note that the above JS was used from another question. I don't fully understand Math.random() and unsure how to randomly display the 50 divs that it does show.
Note: Solution must be pure client side code.
To re-order the divs on the page, you will need to re-append them in the shuffled order. What you're currently doing is getting the elements, selecting 50 of them and showing those (in random sequence, but not changing their position in the dom).
Instead of $(divs).show(); use
$(divs).appendTo(questionContainer).show();
Also notice that you shouldn't use sort() to shuffle an array.
first, create an array containing 0-99 (if you have 100 questions)
var arr = new Array();
for(var i=0; i < 100; i++){
arr[i] = i;
}
then shuffle the array
var shuffle = function(o){ //v1.0
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;
};
var shuffled = shuffle(arr);
then loop through the shuffled array showing the first 50.
for(var i = 0; i < 50; i++){
var thisOne = $($("div.question")[shuffled[i]]);
thisOne.parent().append(thisOne);
thisOne.show();
}
I cannot guarantee this will copy paste in and work, but it should get you started.
It sounds like you actually want to move the elements around on the page.
Assuming the questions are within a container div, something like this might do the trick:
var $divs = $("div.question").sort(function(){
return Math.round(Math.random())-0.5;
}).slice(0,4)
// remove them
$divs.detach();
// reposition them
$('#questionContainer').append($divs);
// show
$divs.show();
I have twelve squares and six of those are selected randomly and a style then applied. This is achieved by generating random values from an array of unique values. The problem is the random values being generated aren't unique e.g 5 could be chosen more than once. I've looked at so many posts on here and there seems to be a number of different ways of achieving this - shuffling etc. Which would be the most effective?
for (var i = 0; i < 6 ; i++)
(function (i) {
setTimeout(function () {
var rand = arr[Math.floor(Math.random() * arr.length)];
var square = document.getElementById('square' + rand);
square.style.background = black;
if (i == 6) Reset();
}, 1500 * i);
})(i);
};
Just for the sake of simplicity I will assume your elements have a class.
Example on jsFiddle
So, I would grab all elements:
var elements = document.getElementsByClassName("square");
Then I would create an array with the IDs
var ids = [];
for (var i = 0; i < elements.length; ++i)
{
ids.push(elements[i].getAttribute("id"));
}
And then generate random numbers on the length of the array
var random = roundingFunction(Math.random() * ids.length);
Then retrieve the element at the index and remove from the array
var elementID = ids.splice(random, 1);
And repeat.
There are bascially three different approaches:
Pick an item by random and repeat if it was used before.
Put all items in an array, pick by random and remove from the array.
Put all items in an array and shuffle.
Which one would be the most efficient can't be answered in the scope of your question. The difference between them is so small that it negligible considering how much the performance differs between browsers.
If you really need the most efficient, you have to try them all and test the performance in a wide variety of browser.
Otherwise just pick the one that seems easiest to implement. The are all good enough for any normal application.
I modified the jQuery Autocomplete implementation to generate autocorrect suggestions too. I used Levenshtein distance as the metric to decide closest matches.
My code runs at each keypress after the jQuery autocomplete does not have any more suggestions. Here's the code that I wrote:
// Returns edit distance between two strings
edit_distance : function(s1, s2) {
// Auxiliary 2D array
var arr = new Array(s1.length+1);
for(var i=0 ; i<s1.length+1 ; i++)
arr[i] = new Array(s2.length+1);
// Algorithm
for(var i=0 ; i<=s1.length ; i++)
for(var j=0 ; j<=s2.length ; j++)
arr[i][j] = 0;
for(var i=0 ; i<=s1.length ; i++)
arr[i][0] = i;
for(var i=0 ; i<=s2.length ; i++)
arr[0][i] = i;
for(var i=1 ; i<=s1.length ; i++)
for(var j=1 ; j<=s2.length ; j++)
arr[i][j] = Math.min(arr[i-1][j-1] + (s1.charAt(i-1)==s2.charAt(j-1) ? 0 : 1), arr[i-1][j]+1, arr[i][j-1]+1);
// Final answer
return arr[s1.length][s2.length].toString(10);
},
// This is called at each keypress
auto_correct : function() {
// Make object array for sorting both names and IDs in one go
var objArray = new Array();
for(var i=0 ; i<idArray.length ; i++) {
objArray[i] = new Object();
objArray[i].id = idArray[i];
objArray[i].name = nameArray[i];
}
// Sort object array by edit distance
var out = this;
companyObjArray.sort (
function(a,b) {
var input = jQuery("#inputbox").val().toLowerCase();
var d1 = a.name.toLowerCase();
var d2 = b.name.toLowerCase();
return out.editDistance(input,d1) - out.editDistance(input,d2);
}
);
// Copy some closest matches in arrays that are shown by jQuery
this.suggestions = new Array();
this.data = new Array();
for(var i=0 ; i<5 ; i++) {
this.suggestions.push(companyObjArray[i].name);
this.data.push(companyObjArray[i].id);
}
}
All names have IDs associated with them, so before sorting I'm just creating an object array out of them, and sorting the array.
Since the list to search from is in thousands, its slow. I found a data structure called a BK-tree, which can speed it up, but I'm can't implement it right now. I'm looking for optimization suggestions to speed this up. Any suggestions are welcome. Thanks in advance.
EDIT. I decided to use Sift3 as my string distance metric instead of Levenshein distance, it gives more meaningful results and is faster.
There are quite a few things you can optimize here, but most of it boils down to this: Only do each of your 'heavier' calculations once. You are doing a lot of work on each keypress, in every sort comparison, etc., and caching those values will help a lot.
However, for a significant added performance boost, there is another, quite nifty optimization trick you can use. It is used by every major search engine, including on-site search engines on sites such as Amazon.com.
It takes advantage of the fact that you really don't need to sort the whole list, since all you're displaying are the top 10 or 12 items. Whether the rest of the thousands of list items are in the correct order doesn't matter in the least. So all you really have to keep track of while running through the list, are the top 10 or 12 items you've seen so far, which, as it turns out, is a lot faster than full sorting.
Here's the idea in pseudocode:
The user types a character, creating a new search term
We define an empty shortlist array of length 12 (or however many suggestions we want)
Loop through the full list of words (we will call it the dictionary):
Calculate the (Levenshtein) edit distance between the dictionary word and the search term
If the distance is lower (better) than the current threshold, we:
Add the word to the top of the shortlist
Let the bottom word in the shortlist 'overflow' out of the list
Set our threshold distance to match the word now at the bottom of the shortlist
When the loop has finished, we have a shortlist containing some of the best words, but they are unsorted, so we sort just those 12 words, which will be really fast.
One small caveat: Depending on the dataset and the number of elements in your shortlist, the shortlist may differ slightly from the actual top elements, but this can be alleviated by increasing the shortlist size, e.g. to 50, and just removing the bottom 38 once the final sort is done.
As for the actual code:
// Cache important elements and values
var $searchField = $('#searchField'),
$results = $('#results'),
numberOfSuggestions = 12,
shortlistWindowSize = 50,
shortlist,
thresholdDistance;
// Do as little as possible in the keyboard event handler
$searchField.on('keyup', function(){
shortlist = [];
thresholdDistance = 100;
// Run through the full dictionary just once,
// storing just the best N we've seen so far
for (var i=0; i<dictionarySize; i++) {
var dist = edit_distance(this.value, dictionary[i]);
if (dist < thresholdDistance) {
shortlist.unshift({
word: dictionary[i],
distance: dist
});
if (shortlist.length > shortlistWindowSize) {
shortlist.pop();
thresholdDistance = shortlist[shortlistWindowSize-1].distance;
}
}
}
// Do a final sorting of just the top words
shortlist.sort(function(a,b){
return a.distance - b.distance;
});
// Finally, format and show the suggestions to the user
$results.html('<p>' + $.map(shortlist, function(el){
return '<span>[dist=' + el.distance + ']</span> ' + el.word;
}).slice(0,numberOfSuggestions).join('</p><p>') + '</p>').show();
});
Try the method out in this 12.000 word demo on jsFiddle
So I'm working on this script. When I'm done with it, it should be used for making 2-and-2 groups. But anyway; The 'input' array in the start of the script, will get 22 different inputs from my HTML file. As a standard, I gave them the values 1-22. The thing is my two blocks '1st number' and '2nd number' doesn't work very well: they don't return the right numbers. Cause I want every elev[x] to be used once! Not 2 times, not 0 times, once! And the blocks returns like some of them twice and some of them isn't even used. So how can I fix this?
function Calculate(){
var elev = [];
var inputs = document.getElementsByName("txt");
for(i=0; i<inputs.length; i++) {
elev[i] = {
"Value": inputs[i].value,
"Used": false
};
}
function shuffle(elev){
var len = elev.length;
for(i=1; i<len; i++){
j = ~~(Math.random()*(i+1));
var temp = elev[i];
arr[i] = elev[j];
arr[j] = temp;
}
}
for(var v=0; v<1; v++) {
shuffle(elev);
document.write(elev + '<br/>\n');
}}
Yes, I'm still new at programming and I just wanna learn what I can learn.
Problem solved by doing the Fisher-Yates shuffle.
The idea of shuffling an array and iterating over it is correct, however, sorting by a coin-flipping comparator (a common mis-implementation; suggested by the other answer) is not the correct way to shuffle an array; it produces very skewed results and is not even guaranteed to finish.
Unless you're willing to fetch underscore.js (arr = _.shuffle(arr)), It is recommended to use the Fisher-Yates shuffle:
function shuffle(arr){
var len = arr.length;
for(var i=1; i<len; i++){
var j = ~~(Math.random()*(i+1)); // ~~ = cast to integer
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
...
shuffle(elev);
for(var i=0; i<elev.length; i++){
//do something with elev[i]
console.log(elev[i].Value);
}
also, note that object fields should be lowercase, not uppercase (value', not 'Value'). Also, theUsed` field should not be neccessary since you now iterate in order. Also, any chance you could use an array of Values? They seem to be the only field in your objects.
Also, Don't use document.write(). It doesn't work as expected when used after the page load. If you want to append something to the document, you hate jQuery and other frameworks and you don't want to go the long way of creating and composing DOM nodes, document.body.innerHTML += is still better than document.write() (but still consider appending DocumentFragments instead').
demo: http://jsfiddle.net/honnza/2GSDd/1/
You are only getting random numbers once, and then processing them if they haven't already been used.
I would suggest shuffling the array (elev.sort(function() {return Math.random()-0.5})) and then just iterating through the result without further random numbers.
Situation
I'm currently writing a javascript widget that displays a random quote into a html element. the quotes are stored in a javascript array as well as how many times they've been displayed into the html element. A quote to be displayed cannot be the same quote as was previously displayed. Furthermore the chance for a quote to be selected is based on it's previous occurences in the html element. ( less occurrences should result in a higher chance compared to the other quotes to be selected for display.
Current solution
I've currently made it work ( with my severely lacking javascript knowledge ) by using a lot of looping through various arrays. while this currently works ( !! ) I find this solution rather expensive for what I want to achieve.
What I'm looking for
Alternative methods of removing an array element from an array, currently looping through the entire array to find the element I want removed and copy all other elements into a new array
Alternative method of calculating and selecting a element from an array based on it's occurence
Anything else you notice I should / could do different while still enforcing the stated business rules under Situation
The Code
var quoteElement = $("div#Quotes > q"),
quotes = [[" AAAAAAAAAAAA ", 1],
[" BBBBBBBBBBBB ", 1],
[" CCCCCCCCCCCC ", 1],
[" DDDDDDDDDDDD ", 1]],
fadeTimer = 600,
displayNewQuote = function () {
var currentQuote = quoteElement.text();
var eligibleQuotes = new Array();
var exclusionFound = false;
for (var i = 0; i < quotes.length; i++) {
var iteratedQuote = quotes[i];
if (exclusionFound === false) {
if (currentQuote == iteratedQuote[0].toString())
exclusionFound = true;
else
eligibleQuotes.push(iteratedQuote);
} else
eligibleQuotes.push(iteratedQuote);
}
eligibleQuotes.sort( function (current, next) {
return current[1] - next[1];
} );
var calculatePoint = eligibleQuotes[0][1];
var occurenceRelation = new Array();
var relationSum = 0;
for (var i = 0; i < eligibleQuotes.length; i++) {
if (i == 0)
occurenceRelation[i] = 1 / ((calculatePoint / calculatePoint) + (calculatePoint / eligibleQuotes[i+1][1]));
else
occurenceRelation[i] = occurenceRelation[0] * (calculatePoint / eligibleQuotes[i][1]);
relationSum = relationSum + (occurenceRelation[i] * 100);
}
var generatedNumber = Math.floor(relationSum * Math.random());
var newQuote;
for (var i = 0; i < occurenceRelation.length; i++) {
if (occurenceRelation[i] <= generatedNumber) {
newQuote = eligibleQuotes[i][0].toString();
i = occurenceRelation.length;
}
}
for (var i = 0; i < quotes.length; i++) {
var iteratedQuote = quotes[i][0].toString();
if (iteratedQuote == newQuote) {
quotes[i][1]++;
i = quotes.length;
}
}
quoteElement.stop(true, true)
.fadeOut(fadeTimer);
setTimeout( function () {
quoteElement.html(newQuote)
.fadeIn(fadeTimer);
}, fadeTimer);
}
if (quotes.length > 1)
setInterval(displayNewQuote, 10000);
Alternatives considered
Always chose the array element with the lowest occurence.
Decided against this as this would / could possibly reveal a too obvious pattern in the animation
combine several for loops to reduce the workload
Decided against this as this would make the code to esoteric, I'd probably wouldn't understand the code anymore next week
jsFiddle reference
http://jsfiddle.net/P5rk3/
Update
Rewrote my function with the techniques mentioned, while I fear that these techniques still loop through the entire array to find it's requirements, at least my code looks cleaner : )
References used after reading the answers here:
http://www.tutorialspoint.com/javascript/array_map.htm
http://www.tutorialspoint.com/javascript/array_filter.htm
http://api.jquery.com/jQuery.each/
I suggest array functions that are mostly supported (and easily added if not):
[].splice(index, howManyToDelete); // you can alternatively add extra parameters to slot into the place of deletion
[].indexOf(elementToSearchFor);
[].filter(function(){});
Other useful functions include forEach and map.
I agree that combining all the work into one giant loop is ugly (and not always possible), and you gain little by doing it, so readability is definitely the winner. Although you shouldn't need too many loops with these array functions.
The answer that you want:
Create an integer array that stores the number of uses of every quote. Also, a global variable Tot with the total number of quotes already used (i.e., the sum of that integer array). Find also Mean, as Tot / number of quotes.
Chose a random number between 0 and Tot - 1.
For each quote, add Mean * 2 - the number of uses(*1). When you get that that value has exceeded the random number generated, select that quote.
In case that quote is the one currently displayed, either select the next or the previous quote or just repeat the process.
The real answer:
Use a random quote, at the very maximum repeat if the quote is duplicated. The data usages are going to be lost when the user reloads/leaves the page. And, no matter how cleverly have you chosen them, most users do not care.
(*1) Check for limits, i.e. that the first or last quota will be eligible with this formula.
Alternative methods of removing an array element from an array
With ES5's Array.filter() method:
Array.prototype.without = function(v) {
return this.filter(function(x) {
return v !== x;
});
};
given an array a, a.without(v) will return a copy of a without the element v in it.
less occurrences should result in a higher chance compared to the other quotes to be selected for display
You shouldn't mess with chance - as my mathematician other-half says, "chance doesn't have a memory".
What you're suggesting is akin to the idea that numbers in the lottery that haven't come up yet must be "overdue" and therefore more likely to appear. It simply isn't true.
You can write functions that explicitly define what you're trying to do with the loop.
Your first loop is a filter.
Your second loop is a map + some side effect.
I don't know about the other loops, they're weird :P
A filter is something like:
function filter(array, condition) {
var i = 0, new_array = [];
for (; i < array.length; i += 1) {
if (condition(array[i], i)) {
new_array.push(array[i]);
}
}
return new_array;
}
var numbers = [1,2,3,4,5,6,7,8,9];
var even_numbers = filter(numbers, function (number, index) {
return number % 2 === 0;
});
alert(even_numbers); // [2,4,6,8]
You can't avoid the loop, but you can add more semantics to the code by making a function that explains what you're doing.
If, for some reason, you are not comfortable with splice or filter methods, there is a nice (outdated, but still working) method by John Resig: http://ejohn.org/blog/javascript-array-remove/