Javascript autocomplete stop for in loop - javascript

I'm trying to build a simple autocomplete feature and I'm running into a problem with the following script
<input type="text" name="search" id="searchField" placeholder="search for something" />
<div class="results-list">
<ul id="searchResults"></ul>
</div>
<script>
var users = JSON.parse('{{ $accounts }}'); // json data from php script
// add event listener
Event.add('searchField', 'keyup', function(){
if(document.getElementById('searchField').value.length > 1) {
processSearch(users, 'searchField', 'searchResults');
}
else {
document.getElementById('searchResults').innerHTML = '';
}
});
function processSearch(data, field, result) {
if(document.getElementById(field).value.length == 0) {
document.getElementById(result).innerHTML = '';
}
else {
for (var k in data) {
if(data.hasOwnProperty(k)) {
if(data[k].indexOf(document.getElementById(field).value) != -1) {
var list = document.createElement('li');
list.innerHTML = data[k];
document.getElementById(result).appendChild(list);
}
}
}
}
}
</script>
Now I'm getting the right results but if I keep writing the results are getting reproduced and I end up with a lot of duplicate results like in the picture below
And when I'm deleting the results are still duplicating unless the length of the field is less than 1 and all results are getting deleted.
Would anyone be able to tell me how to stop getting the duplicates?

The problem is that as processSearch() is called on every keystroke, and you append the results all the time to previous results. You need the delete the previous results in the beginning callback, i.e. iterate over all children of the result element and remove them using removeChild(). And then you can add the new results.

You can use the for...in statement for enumerated objects, but for iterating arrays, for...in should be avoided. Instead use the standard for statement.
for (var k = 0; k < data.length; k++) {
if(data.hasOwnProperty(k)) {
if(data[k].indexOf(document.getElementById(field).value) != -1) {
var list = document.createElement('li');
list.innerHTML = data[k];
document.getElementById(result).appendChild(list);
}
}
}

Related

Javascript For Loop will not increment

I am trying to use a for loop to push values entered by a user into an array. But, for some reason, the loop will not increment to push the next value into the array but will instead overwrite the first location. This is the HTML used to get the user's input below.
<div class="total-budget-fields">
<h3>Enter Budget</h3>
<input type="number" placeholder="$1000" id="budget">
<input type="button" onclick="addNum();" class="btn hit" id="budget" value="Calculate">
</div>
And this here is the javascript function linked to the button below.
addNum = () => {
// console.log('addNum');
var budgetArray = [];
var budget = document.getElementById('budget').value;
for (i=0; ; i++) {
if (budget.trim() == '') {
alert("Field is Empty!");
} else if (!(isNaN(budget))) {
budgetArray.push(budget);
break;
}
}
console.log(budgetArray);
console.log(i);
}
I tried using a while loop as an alternative which didn't work. Any and all help is welcomed, thank you in advanced!
Like already mentioned in the comments, the loop makes non sense and you dont need an index variable like i. Instead make the array global and just push new values. This will increase the size of the array automatically. If budgetArray is in the scope of your function, it is created on every call of this function.
var budgetArray = [];
addNum = ()=>{
var budget = document.getElementById('budget').value;
if (budget.trim() == '') {
alert("Field is Empty!");
} else if (!(isNaN(budget))) {
budgetArray.push(budget);
}
}
Also in your markup file two elements has the same id budget. You should fix that and make your id's unique across your whole document. It currently works because if there is more than one element with the same id, getElementById will just give you the first one.
The first problem is that on every click you are reassigning you budgetArray to be an empty array. This is why you will always have only one item in the array.
You have to cache your array outside the addSum function. As your budget container will not change during the time, it is a good idea to cache it as well.
Also, you do not need for loop for this task at all. So something like this will do the job.
var budgetArray = [];
var budgetContainer = document.getElementById('budget');
addNum = () => {
const budget = budgetContainer.value.trim();
if (budget == '') {
alert("Field is Empty!");
} else if (!(isNaN(budget))) {
budgetArray.push(budget);
}
}
console.log(budgetArray);
console.log(i);

Using an array of numbers to add up to a specific number

I've been trying to find out an efficient method of finding multiple numbers from an array that add up to a given number. In this instance I'm trying to find 3 numbers that total a target number.
I've got a basic working example below but unfortunately the recursive loop fails, it looks like there's an issue with it constantly looping. Ideally it would find the first possible answer and return it, but when it can't find an answer it gets stuck in the loop and breaks the browser.
Warning: the below code will break due to a memory leak:
let array = [5,6,3,3,6,67,2,2,6,7,7,2,1,3,4,5,67,7,4,2,5,6,3,3,6,67,2,2,6,7,7,2,1,3,4,5,67,7,4,2,5,6,3,3,6,67,2,2,6,7,7,2,1,3,4,5,67,7,4,2];
function findSums(arr, target, count) {
var result = [];
function recurse(start, leftOver, selection) {
if (leftOver < 0) return; // failure
if (leftOver === 0 && selection.length == count) {
result.push(selection); // add solution
return;
}
for (var i = start; i < arr.length; i++) {
recurse(i, leftOver-arr[i], selection.concat(arr[i]));
}
}
recurse(0, target, []);
return result;
}
// Demo
$('#input').on('blur', function(e){
let value = parseInt(e.target.value);
let result = findSums(array, value, 3);
if(result.length > 0){
$('#output').text(result[0]);
} else {
$('#output').text('Nothing found');
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>Input Number Below</h1>
<input id="input" type="number" />
<code id="output" ></code>
Well, it didn't break, when I tested, but still here are some tips:
You should set additional limitation to the count. You are making to much extra calls. When your function deals with really big sum, small numbers and small count it will call itself again until it reaches or overflows the desired sum, and only after that it will check current count. So you should add
if (selection.length > count) return;
Also. As I see there are many duplicates in your array, so I assume, that usage of the same number is allowed, but only if it is taken from another index. In your loop you are calling next recurse with the same start index. I think, you need
for (var i = start; i < arr.length; i++) {
recurse(i + 1, leftOver-arr[i], selection.concat(arr[i]));
}
And finally. This will not influence the recursive part of an algorithm, but maybe you'd like to filter out same results, or filter out your array to remove all duplicates.
Hope this helps.
Edit: sorry, missed the part about first possible solution. Here is the way to achieve this:
function recurse(start, leftOver, selection) {
if (leftOver < 0) return false; // failure
if (selection.length > count) return false;
if (leftOver === 0 && selection.length == count) {
result.push(selection); // add solution
return true;
}
for (var i = start; i < arr.length; i++) {
var res = recurse(i + 1, leftOver-arr[i], selection.concat(arr[i]));
if (res) return true;
}
}

Javascript loop doesn't work in meteor

This should work but its not. What am I doing wrong? I want to output "selected" to tags I have on a meteor page
Template.editor.onRendered( function() {
var cats = ["Comedy","Action","Self Help"];
var arrayLength = cats.length;
for (var i = 0; i < arrayLength; i++) {
if(cats[i].indexOf(getDocument.category) != -1){
//found
var id = cats[i].trim().toLowerCase();
$("body").find("#"+id).attr("selected=selected");
console.log(id);
} else {
console.log(getDocument.category)
}
}
}
also
getDocument.category = ["Action", "Comedy"]
Maybe change
$("body").find("#"+id).attr("selected=selected");
with
$("body").find("#"+id).attr("selected","selected");
Edit:
if(cats[i].indexOf(getDocument.category) != -1){
I think you have here a wrong direction
try this instead:
if(getDocument.category.indexOf(cats[i]) != -1){
If I do not mistakenly understand what you asking for, you are trying to find the elements of 'cats' array if exist in the getDocument.category. If so, the above approach is wrong. Take a look at this line:
if(cats[i].indexOf(getDocument.category) != -1){...}
the result of this if checking will always returning -1, the explanation is below:
cats[i] will return the element (with index i) of cats, so if i=0 the result will be "Comedy". Then, indexOf will be executed on it, "Comedy".indexOf() ,
to find the position of getDocument.category (which is an array).
That's means you are looking for an array inside a string? that's will not work.
Actually, we can check if an element exists in array with includes methods. So maybe the complete code will be looked like this:
Template.editor.onRendered(function() {
var cats = ["Comedy", "Action", "Self Help"];
var arrayLength = cats.length;
for (var i = 0; i < arrayLength; i++) {
if (getDocument.category.includes(cats[0])) {
//found
var id = cats[i].trim().toLowerCase();
$("body").find("#" + id).attr("selected=selected");
console.log(id);
} else {
console.log(getDocument.category)
}
}
})
Hope this will help, thanks
You need to change a line to set selected attribute
$("body").find("#"+id).attr("selected","selected");
Or try the following:
$(document).find("#"+id).attr("selected","selected");

Checking Postal Codes with JQuery

I try to build a form which checks if an entered zip code matches a zip code in a predefined array. I don't use a DB, it's all very basic and hardcoded, but should be sufficient in this case.
The problem is that only the first zip-code in the array ('83512') works. If i am entering the second one ('83533') the code spits out "no success".
What am I doing wrong?
Thanks in advance.
HTML:
<form action="javascript:alert('success.');" id="checkplz">
<label for="plz">ZIP:</label>
<input type="text" name="plz" id="plz" />
<button id="submit" >Check!</button>
<div id="output"></div>
</form>
JQuery:
$(document).ready(function(){
var list = ['83512','83533'];
$("#checkplz").submit(function() {
for (var i = 0; i < list.length; i++) {
if ($("#plz").val() == list[i]) {
$("#output").append("<strong class='success'>success!</strong>").show();
return true;
}
$("#output").text("no success!").show().fadeOut(10000);
return false;
}
});
});
The logic in your loop is off. See below:
$(document).ready(function(){
var list = ['83512','83533'];
$("#checkplz").submit(function() {
var match = false;
for (var i = 0; i < list.length; i++) {
if ($("#plz").val() == list[i]) {
$("#output").append("<strong class='success'>success!</strong>").show();
return true;
}
}
$("#output").text("no success!").show().fadeOut(10000);
return false;
});
});
The problem is within the logic of your loop. The loop will only run one time, because the loop will always return after the first iteration (true if it finds the first element in the list array, false for everything else), rather than continuing through all iterations. So, what is happening for the second element is that the loop is running, determining that the first element was not found and returning false and never even processing the second element.
A better way to do this would be to loop the list array until you find a matching element, and keep track of wether an match was found or not. This will make sure we don't drop out of the loop until we've processed all elements of the array (or found a match, in which case we can stop the loop to save on processing).
See below (http://jsfiddle.net/ryanbrill/Kws7A/) for some example code with a few comments about what is happening.
$(document).ready(function(){
var list = ['83512','83533'];
$("#checkplz").submit(function() {
var matched = false; // Set up variable to track if we find a match
$(list).each(function() {
// Inside the jQuery 'each' method, 'this' equals the current item in the iteration.
if(this == $("#plz").val()) {
matched = true; // set the 'matched' variable to true
$("#output").append("<strong class='success'>success!</strong>").show();
return; // Since we found a match, we can stop processing the array
}
});
// Outside of the loop, only display no success if we didn't find any matches.
if (!matched) {
$("#output").text("no success!").show().fadeOut(10000);
}
});
});
Try returning false outside the loop, so it will only happen once all values are checked:
$(document).ready(function(){
var list = ['83512','83533'];
$("#checkplz").submit(function() {
for (var i = 0; i < list.length; i++) {
if ($("#plz").val() == list[i]) {
$("#output").append("<strong class='success'>success!</strong>").show();
return true;
}
}
$("#output").text("no success!").show().fadeOut(10000);
return false;
});
});
Use jQuery.inArray()
var list = ['83512','83533'];
if($.inArray('83533', list) > -1){
// found
}
Docs here: http://api.jquery.com/jQuery.inArray/

Why is my JS/jQuery loop stopping after 2 loops?

So I have an array with a lot of repeat values. myArr = ['yeh','yeh','yeh','hey']. I have an HTML form that allows the user to type in a word and if that word is in the array it'll delete all the repeat values and print out the new myArr without those values. However, my loop keeps stopping after a few loops without finishing deleting the rest of the duplicates. like if I had ['yeh','yeh','yeh'] and typed "yeh" in the form, it only deletes two of the 'yeh'.
HTML
<form id="remove_user" action="#" method="post">
<label for="user_num">Remove user:</label>
<input type="text" id="user_num" name="user_num" placeholder="number">
<input type="submit" value="(-) remove">
</form><!-- #remove_user -->
<ul id="user_list"></ul><!-- #user_list -->
JS:
$('#remove_user').submit(function(){
id = $('#user_num').val();
$.each(myArr, function(i, value){
if (value == id){
myArr.splice(i, 1);
console.log(myArr);
}
});
console.log(myArr);
$('#user_list').html('');
for (var i=0; i < myArr.length; i += 1) {
console.log(myArr[i]);
$('#user_list').append('<li>' +myArr[i]+ '</li>');
};
return false;
});
UPDATED ANSWER (so as said below, I was modifying it while looping over it, so things were getting skipped. Instead of what I did before, I used grep to create a new array of all the values that didn't match the submitted value, then just went through diffValues elements one by one and printed each out):
$('#remove_user').submit(function(){
id = $('#user_num').val();
var diffValues = $.grep(myArr, function(value, i){
return value != id;
});
console.log(diffValues);
$('#user_list').html('');
for (var i=0; i < diffValues.length; i += 1) {
console.log(diffValues[i]);
$('#user_list').append('<li>' +diffValues[i]+ '</li>');
};
return false;
});
When you splice the array, you are changing that array. So basically your array is going through these steps:
['yeh','yeh','yeh','hey']
^^^
splice, move iterator forward
['yeh','yeh','hey']
^^^
splice, move iterator forward
['yeh','hey']
^^^
end loop - one of the 'yeh' is still there
You might be interested in .grep().
If you want to delete values in an array that you're actively looping with, you have to go through it backwards (as making the length of the array shorter that way wouldn't invalidate your loop).
id = $('#user_num').val();
for (var i = myArr.length - 1; i >= 0; i--) {
if (myArr[i] == id) {
myArr.splice(i, 1);
}
}

Categories