I have a JavaScript variable that holds an array of dictionary words like
var words = ['and','cat', n1, n2, n3 and so on ]
This array holds about 58020 words.
What i have done is created an auto complete jQuery plugin that displays the words from the dictionary array in a drop down list when the user starts typing text into the text box. But the browser crashes at some point because I think the looping through each word is making the process slow.
How can i overcome this?
Here is the function that checks the word array and outputs the words if found
$(textInput).keyup(function(e) {
var text = $(this).val();
var foundTag = false;
for (var i = 0; i < settings.tags.length; i++) {
var tagName = settings.tags[i].toLowerCase();
if (tagName.startsWith(text)) {
if (text != '') {
foundTag = true;
$(settings.tagContainer).append(GetDivDropDownItem(settings.tags[i]));
}
else {
}
}
}
});
Edit
$(textInput).keyup(function(e) {
var text = $(this).val();
var foundTag = false;
for (var i = 0; i < settings.words.length; i++) {
var tagName = settings.words[i].toLowerCase();
if (tagName.startsWith(text)) {
if (text != '') {
foundTag = true;
$(settings.tagContainer).append(GetDivDropDownItem(settings.words[i]));
}
else {
}
}
}
});
var GetDivDropDownItem = function(text) {
var cWidth = $(container).css("width");
cWidth = cWidth.split("px")[0];
var tag = $("<div/>");
$(tag).css("paddingLeft", "5px");
$(tag).css("paddingRight", "5px");
$(tag).css("paddingBottom", "5px");
$(tag).css("paddingTop", "5px");
$(tag).css("width", cWidth - 10);
$(tag).css("float", "left");
$(tag).css("fontFamily", "Arial");
$(tag).css("fontSize", "12px");
$(tag).css("color", "#6A6B6C");
$(tag).text(text);
return $(tag);
};
You need to use better datastructures and algorithms. In general, I would suggest doing some research on pre-existing work before trying to tackle any problem.
This is an article that may be of help: http://orion.lcg.ufrj.br/Dr.Dobbs/books/book5/chap08.htm
See this benchmarks and comparisons done by jQuery creator John Resig:
http://ejohn.org/blog/revised-javascript-dictionary-search/
Basically the answer is a simple trie structure, if you really want to do it pure-JS.
By not putting 58.000 words in a Javascript array.
Use a webservice that holds all the dictionary words in a database, and query that.
edit: If you really insist on storing this in a javascript array, group the words by their first two characters. Easy to implement and around 600 times faster already.
A trie data structure would be good for a dictionnary.
Assuming settings.tags is the array of dictionary words, this code is going to be very cumbersome, since you're looping through the entire array with each keyup event.
I would suggest that you organize the dictionary words in a structure that allows you to go to the words very quickly. Perhaps a binary tree or just an associative array.
Related
I'm trying to clone a div after a user puts in the amount of divs to be cloned. User will put in a number (say 3) and the function will create three group-container divs. The prompt works, but nothing happens after that. Seems pretty simple but it's evading me. Is my logic incorrect? Obviously my programming skills are very new.
I create a function that has the input (groupInput)
Create a for loop to reiterate the following instruction
The for loop will clone group-container as many times as i<groupInput
function addGroup() {
var groupInput = prompt("How many groups? 1-100");
for(i=0; i<groupInput; i++){
var group = document.getElementById("group-container");
var clone = group.cloneNode(true);
group.parentNode.appendChild(clone);
}
}
Any suggestions would be much appreciated.
Updated
Thanks for the suggestions, I get I should use class for this now.
I did get it to work with the ID in jsfiddle (not sure why it's not in my html), but now with the class it's not: https://jsfiddle.net/waynebunch/c5sw5dxu/. getElementsByClassName is valid right?
You should put the group declaration outside of the for loop so the clone remains the same throughout the loop.
Fiddle
function addGroup() {
var groupInput = prompt("How many groups? 1-100");
var group = document.getElementById("group-container");
for(i=0; i<groupInput; i++){
var clone = group.cloneNode(true);
group.parentNode.appendChild(clone);
}
}
The prompt() method probably returns the correct number, but with type set to String. Instead try
parseInt(groupInput)
To convert the value to a number, which should allow the for loop to execute properly.
Something like the below might work once you get your quantity in from a prompt or text input.
var doc = document;
var input = prompt("Please enter your qty", "0");
if (input != null) {
for (i = 0; i < input; i++) {
var elem = doc.createElement('div');
elem.className = 'group-container';
}
}
I have a big question.
I have many Strings in my Programm and want to check these Strings on there values.
I wrote a Loop for it, but insted of the Definition of an String he is creating a new value. It's basicly really difficult to discribe, also because i am basicly German.
But i can give you my current code, so maybee you will see what I mean:
{
var Loch1G = $('#m1-Rundenanalyse-Datum').val(); //In the strings just the number is changing
var Loch2G = $('#m1-Rundenanalyse-Turnier').val();
x=1
while (x <= 2) {
if ("Loch" + x + "G" == ""){ //Next String is genrated (x=x+1)
alert("Eingabe war leer");
}
x=x+1
}
}
How can I solve this?
I'd suggest using an array to store the values you want to check:
var lochs = [];
lochs.push($('#m1-Rundenanalyse-Datum').val());
lochs.push($('#m1-Rundenanalyse-Turnier').val());
for (var i = 0, len = lochs.length; i < len; i++){
if (lochs[i] == ''){
alert("Eingabe war leer");
}
}
JS Fiddle demos: passes (no alert), fails (alert)
This suggestion is based on my presumption that you're trying to create the names of the vars you want to check, which won't work, whereas this approach lets you store all values (however many) in the same array and then iterate over that array to find any values that are equal to an empty string.
If you really want to stick with your current approach, you could do the following:
{
window.Loch1G = $('#m1-Rundenanalyse-Datum').val(); //In the strings just the number is changing
window.Loch2G = $('#m1-Rundenanalyse-Turnier').val();
var x=1;
while (x <= 2) {
if (window["Loch" + x + "G"] == ""){ //Next String is genrated (x=x+1)
alert("Eingabe war leer");
}
x=x+1;
}
}
But I can't think why you'd want to; plus the use of global variables is poor practice as it explicitly makes those variables available to every closure within the document, which allows them to be easily, and accidentally, overwritten.
In a reasonably up-to-date browser, that implements Array.prototype.every, you could dispense with the explicit iteration:
var lochs = [];
lochs.push($('#m1-Rundenanalyse-Datum').val());
lochs.push($('#m1-Rundenanalyse-Turnier').val());
if (!lochs.every(function(a){ return a !== ''; })) {
alert("Eingabe war leer");
}
JS Fiddle demos: passes (no alert), fails (alerts).
a fairly simple question today.
I have an object that looks like this:
var buttonLogos = {
adcraft: [".","..","1.png","2.png","3.png"],
ferries: [".","..","1.png","2.png"]
}
and I'm looking for a quick way to remove the entries at the beginning with the dots, I would usually just filter out anything with a dot, but I can't because the strings I want contain a .png
it might be a solution to filter out the first two entries, because they will always be "." and ".." but alas I'm not sure how to even do that.
(jQuery is encouraged)
I would love some help! Thanks.
for(i in buttonLogos){
buttonLogos[i] = buttonLogos[i].filter(function(i){
return !i.match(/^\.{1,2}$/);
});
}
You can use js regex as follows,
buttonLogos.adcraft = $(buttonLogos.adcraft).filter(function(i,val){return val.match(/[^\.]/);});
Filters as mentioned in other answers or a combination of indexOf and splice would also work.
var adcraft = [".","..","1.png","2.png","3.png"];
var elems_to_rm = [];
for (var i = 0; i < adcraft.length; i++) {
if (adcraft[i].indexOf('.') === 0) {
elems_to_rm.push(adcraft[i]);
}
}
for (var i = 0; i < elems_to_rm.length; i++) {
var index = adcraft.indexOf(elems_to_rm[i]);
adcraft.splice(index, 1);
}
Try manually. Any number of items can be deleted from the array by specifying just two
arguments: the position of the first item to delete and the number of items to delete. For
example, splice(0, 2) deletes the first two items.
This is probably easy for someone.
I am returning a list of campaignIDs (12,45,66) via JSON to a javascript variable
var campaignList = res.DATA.CAMPAIGNS
Now, given a specified campaignID passed in the URL
var campaignId ='<cfoutput>#url.campaignID#</cfoutput>'
I want to check if the returned list contains this campaignID
Any help much appreciated.
Plenty of ways to do it, but I like nice data structures, so ...
Split the list on comma, then loop over list, looking for value:
function campaignExists(campaignList,campaignId) {
aCampaignList = campaignList.split(',');
for (i=0;i<aCampaignList.length;i++) {
if (aCampaignList[i]==campaignId)
return true;
}
return false;
}
Since Array.indexOf sadly isn't cross browser, you're looking at something like:
// assume there is no match
var match_found = false;
// iterate over the campaign list looking for a match,
// set "match_found" to true if we find one
for (var i = 0; i < campaignList.length; i += 1) {
if (parseInt(campaignList[i]) === parseInt(campaignId)) {
match_found = true;
break;
}
}
If you need to do this repeatedly, wrap it in a function
Here's a bit of a "out of the box" solution. You could create a struct for your property id's that you pass into the json searilizer have the key and the value the same. Then you can test the struct for hasOwnProperty. For example:
var campaignIDs = {12 : 12, 45 : 45, 66 : 66};
campaignIDs.hasOwnProperty("12"); //true
campaignIDs.hasOwnProperty("32"); //false
This way if the list is pretty long you wont have to loop through all of the potential properties to find a match. Here's a fiddle to see it in action:
http://jsfiddle.net/bittersweetryan/NeLfk/
I don't like Billy's answer to this, variables within the function have been declared in the global scope and it is somewhat over complicated. If you have a list of ids as a string in your js just search for the id you have from user input.
var patt = new RegExp("(^|,)" + campaignId + "(,|$)");
var foundCampaign = campaignList.search(patt) != -1;
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/