jquery indexOf replacement - javascript

Here I have a hidden input field
<input type="hidden" value="php,php mysql" id="tags"/>
and I also have a normal input box which users could add their new tags, now I want to check if the new tag which user wants to add is already added for him or not, if it's already added, alert('already there');
here is my code:
var already_added = $('#tags_final').val().toLowerCase();
new_tag = new_tag.toLowerCase();
if (already_added.indexOf(new_tag) < 0){
// add it, everything is OK
}else{
alert('already there');
}
the above works just fine for normal values, for example now if i try to add "php", this is gonna alert('already there'), the problem is, if I add "mysql", this also sends the alert since it finds it in "php mysql", but "mysql" is another tag and it needs to be added. what's solutions come to mind for this?
thanks for your help

I would think you'd want to break this up into separate pieces and perform a full text comparison on the actual tag itself.
var tags = $('#tags_final').val().toLowerCase().split(',');
new_tag = new_tag.toLowerCase();
if ($.inArray(new_tag, tags) < 0) {
// add it
} else {
alert('already there');
}
var tags = $('#tags_final').val().toLowerCase().split(',');
new_tag = new_tag.toLowerCase();
if (tags.indexOf(new_tag) < 0) {
// add it
} else {
alert('already there');
}
Edit: Courtesy of #nybbler, this would be more accurate using the jquery inArray method as linked in his comment.

I think using indexOf directly on the string is faster than either a regex or splitting into an array (even more so when relying on $.inArray).
Simply wrap your already_added string of tags with commas, then do the same for the tag when searching for it with indexOf:
var already_added = ',' + $('#tags_final').val().toLowerCase() + ',';
new_tag = new_tag.toLowerCase();
if (already_added.indexOf(',' + new_tag + ',') < 0) {
// add it
} else {
alert('already there');
}
I got this idea from a similar trick used by jQuery.

Save the tags between braces [php][php mysql]
and search for it
var already_added = $('#tags_final').val().toLowerCase();
new_tag = new_tag.toLowerCase();
if (already_added.indexOf("["+new_tag+"]") < 0){
// add it, everything is OK
}else{
alert('already there');
}

You can use a function like this to look for a key in a comma separated string of keys:
function contains(keys, key) {
var i = 0, k = key.length;
for (var i = 0;(i = keys.indexOf(key, i)) != -1; i += k) {
if ((i == 0 || keys.charAt(i - 1) == ',') && (i + k == keys.length || keys.charAt(i + k) == ',')) {
return true;
}
}
return false;
}
Demo: http://jsfiddle.net/Guffa/UHH9V/

Related

Implement Split method with a function

I want to implement the split method with a function
This is what i am trying to achieve
var string = 'aa,bb,c';
var separator = ',';
var stringList = string.split(separator);
function splitString() {
console.log(stringList);
}
This returns this array  
["aa", "bb", "c"]
I am trying to implement the same with a function but it returns an empty array [] and not ["aa", "bb", "c"]
I have created a jsbin for who can help out.
function split(string,separator) {
var cache = [];
var cachInt = 0;
var lastWord = '';
for (var i = 0; i < string.length; i++) {
if(string[i] === separator) {
cachInt++
lastWord = ''
}
else {
lastWord = lastWord + string[i];
cache[cachInt] == lastWord;
}
}
return cache;
}
function splitString() {
console.log(split('string, separator',','));
}
You do this:
cache[cachInt] == lastWord;
Which should be, because you're not comparing, you're assigning:
cache[cachInt] = lastWord;
While we're at it, there is room for improvement. Your version has the line mentioned above. That line gets run every iteration of i. Thats not really needed, as you only want to perform a save on a split:
if(string[i] === separator) {
cache[cachInt] = lastWord; // Only assign when we find a seperator
cachInt++
lastWord = ''
} else {
lastWord = lastWord + string[i];
}
This has a tiny issue: The last part of string often doesn't have the seperator, it's a,b,c and not a,b,c,.
We can fix that easily with a check after the for to see if you have anything remaining:
if( lastWord!=='' ){
cache[cachInt] = lastWord;
}
return cache;
This has the added feature that it works as a rtrim() (wether you want that or not is up to you to fix).
Also, if you don't need to support older IE versions, then don't use var, use let. If you want to know why, this question explains it well.
Then, you're using a counter to remember which cachInt to use. As we now only use it once per "cacheInt", eg once per word, we know that each addition is +1, and only happens once per word. We also don't really care about the index, we just want each word to be added once. So, you can do cache[] = lastWord, or use push, which is slightly neater: cache.push(lastWord).
By removing the use for this counter, you can also remove the cachInt++ and the let/var cachInt at the beginning of the function, resulting in smaller code.
Result of all of the above:
https://jsbin.com/mejayuv/1/edit?html,js,console,output

Loop through array checking for indexOf's more simple?

Okay, like the title says. I have a array looking like this:
var hiTriggers = new Array();
hiTriggers = ["hi", "hai", "hello"];
And I'd like to check through it if it finds either of those. I can already achieve this by doing the following:
if(message.indexOf("hi") >= 0) {
// do whatever here!
}
But I'm looking for an more efficient way rather than doing 100 if() checks. Such as loop through an array with the "hiTriggers".
I tried the following:
for(var i; i < hiTriggers.length; i++) {
console.log(hiTriggers[i]); // simply to know if it checked them through)
if(message.indexOf(hiTriggers[i]) >= 0) {
//do stuff here
}
}
Which sadly did not work as I wanted as it does not check at all.
Thanks in advance and I hope I made sense with my post!
Edit; please note that I have 'messaged' already 'declared' at another place.
It doesn't run because you didn't give the i variable an initial value. It is undefined.
Change to use var i=0;:
for(var i=0; i < hiTriggers.length; i++) {
//console.log(hiTriggers[i]); // simply to know if it checked them through)
if(message.indexOf(hiTriggers[i]) >= 0) {
//do stuff here
console.log("found " + hiTriggers[i]);
}
}
Try using a regular expression to match the message. The \b is a word boundary marker, and the words between the | characters are what is being searched for. If any of the words appear in the message, then message.match will return the array of matches, otherwise null.
var pattern = /\b(Hello|Hi|Hiya)\b/i;
var message = "Hello World";
if (message.match(pattern))
{
console.log("do stuff");
}
You can write even simpler using a for in loop:
for(var v in hiTriggers){
if(message.indexOf(hiTriggers[v]) >= 0) {
//do stuff here
console.log("found " + hiTriggers[v]);
}
}
Problem is becoz - you have not initialized your var i, make it var i = 0;
You can try forEach loop.
hiTriggers.forEach(function(e) {
if(message.indexOf(e) >= 0) {
//do sthg here
}
})

Javascript if value is in array else in next array

I have found a few posts on here with similar questions but not entirely the same as what I am trying. I am currently using a simple if statement that checks the data the user enters then checks to see if it starts with a number of different values. I am doing this with the following:
var value = string;
var value = value.toLowerCase();
country = "NONE";
county = "NONE";
if (value.indexOf('ba1 ') == 0 || value.indexOf('ba2 ') == 0 || value.indexOf('ba3 ') == 0) { //CHECK AVON (MAINLAND UK) UK.AVON
country = "UK";
county = "UK.AVON";
} else if(value.indexOf('lu') == 0){//CHECK BEDFORDSHIRE (MAINLAND UK) UK.BEDS
country = "UK";
county = "UK.BEDS";
}
I have about 20-30 different if, else statements that are basically checking the post code entered and finding the county associated. However some of these if statements are incredibly long so I would like to store the values inside an array and then in the if statement simply check value.indexOf() for each of the array values.
So in the above example I would have an array as follows for the statement:
var avon = new Array('ba1 ','ba 2','ba3 ');
then inside the indexOf() use each value
Would this be possible with minimal script or am I going to need to make a function for this to work? I am ideally wanting to keep the array inside the if statement instead of querying for each array value.
You can use the some Array method (though you might need to shim it for legacy environments):
var value = string.toLowerCase(),
country = "NONE",
county = "NONE";
if (['ba1 ','ba 2','ba3 '].some(function(str) {
return value.slice(0, str.length) === str;
})) {
country = "UK";
county = "UK.AVON";
}
(using a more performant How to check if a string "StartsWith" another string? implementation also)
For an even shorter condition, you might also resort to regex (anchor and alternation):
if (/^ba(1 | 2|3 )/i.test(string)) { … }
No, it doesn’t exist, but you can make a function to do just that:
function containsAny(string, substrings) {
for(var i = 0; i < substrings.length; i++) {
if(string.indexOf(substrings[i]) !== -1) {
return true;
}
}
return false;
}
Alternatively, there’s a regular expression:
/ba[123] /.test(value)
My recomendation is to rethink your approach and use regular expressions instead of indexOf.
But if you really need it, you can use the following method:
function checkStart(value, acceptableStarts){
for (var i=0; i<acceptableStarts.length; i++) {
if (value.indexOf(acceptableStarts[i]) == 0) {
return true;
}
}
return false;
}
Your previous usage turns into:
if (checkStart(value, ['ba1', ba2 ', 'ba3'])) {
country = 'UK';
}
Even better you can generalize stuff, like this:
var countryPrefixes = {
'UK' : ['ba1','ba2 ', 'ba3'],
'FR' : ['fa2','fa2']
}
for (var key in countryPrefixes) {
if (checkStart(value, countryPrefixes[key]) {
country = key;
}
}
I'd forget using hard-coded logic for this, and just use data:
var countyMapping = {
'BA1': 'UK.AVON',
'BA2': 'UK.AVON',
'BA3': 'UK.AVON',
'LU': 'UK.BEDS',
...
};
Take successive characters off the right hand side of the postcode and do a trivial lookup in the table until you get a match. Four or so lines of code ought to do it:
function getCounty(str) {
while (str.length) {
var res = countyMapping[str];
if (res !== undefined) return res;
str = str.slice(0, -1);
}
}
I'd suggest normalising your strings first to ensure that the space between the two halves of the postcode is present and in the right place.
For extra bonus points, get the table out of a database so you don't have to modify your code when Scotland gets thrown out of leaves the UK ;-)

jQuery auto correction acts weird on duplicated fields

I am using a script to auto-correct input on forms using jQuery. For example, when someone writes "abc" as his initials, the field will auto-correct the input directly to A.B.C.
These scripts work excellent. However, anyone can fill out several forms with several names. I am using knockout to duplicate the forms. So far so good, but auto-correction doesn't work on duplicated fields anymore..
The auto-correction looks like this (small part):
// Lowercase
$(".lowercase").keyup(function(e)
{
$(".lowercase").val(($(".lowercase").val()).toLowerCase());
if (/[a-z]/g.test(this.value))
{
this.value = this.value.replace(/[^a-z ]/g, '');
}
});
// Initials
$(".initials").focus(function() {
var current = $(".initials").val();
$(".initials").keyup(function(e) {
var key = String.fromCharCode(e.keyCode);
if (key >= 'A' && key <= 'Z') {
current += key + ".";
this.value = current;
}
else {
current = "";
}
});
$(".initials").blur(function() {
var i = $(".initials").val();
var last = i[i.length - 1];
if (last != "." && i.length !== 0){
this.value += ".";
}
});
});
// Capitalize
$(".cap").keyup(function(e)
{
function convertToUpper() {
return arguments[0].toUpperCase();
}
val = this.value.toLowerCase().replace(/\b[a-z]/g, convertToUpper);
this.value = val;
});
A fiddle can be found here
Update
Thanks to raghaw Numbers now work. But other fields don't yet.
You are binding event that is not working on elements that get created in future. Here is the change I made to your code:
$(document).on("keyup", ".numbers", function(e)
// $(".numbers").keyup(function(e)
Your modified fiddle is here http://jsfiddle.net/QUxyy/9/

Error in javascript recursive function

I have the following piece of code (for the present case it may be considered as a function to remove the attributes from a valid html string fed as input):
function parse(htmlStr)
{
console.log(htmlStr);
result+="<"+htmlStr.tagName.toLowerCase()+">";
var nodes=htmlStr.childNodes;
for(i=0;i<nodes.length;i++) {
var node=nodes[i];
if(node.nodeType==3) {
var text=$.trim(node.nodeValue);
if(text!=="") {
result+=text;
}
}
else if(node.nodeType==1) {
result+=parse(node);
}
}
result+="</"+htmlStr.tagName.toLowerCase()+">";
return result;
}
But it is not working as expected. For example, in the following case when I feed it the following html as input:
<div id="t2">
Hi I am
<b>
Test
</b>
</div>
it returns <div>Hi I am<div>Hi I am<b>Test</b></div>.
Also the page crashes if some large input is given to the function.
NOTE: I know there are better implementations of removing attributes from a string using jQuery, but I need to work with the above function here & also the complete code is not for removing attributes, the above is just a shortened part of the code
There is something wrong with your result variable. It is undefined and global. In each recursion you would append the same string to itself, which also makes it crashing for huge inputs. (I can't reproduce anything, it crashes right away with a Undefined variable Error)
BTW: Your argument is no htmlStr, it is a domNode. And you're not parsing anything. Please don't use wrong self-documenting variable names.
Corrected version:
function serialize(domElement) {
var tagname = domElement.tagName.toLowerCase();
var result = "<"+tagname+">";
// ^^^ ^ not a +=
var children = domElement.childNodes;
for (var i=0; i<children.length ;i++) {
// ^^^ was also missing
if (children[i].nodeType == 3) {
result += children[i].data;
} else if (children[i].nodeType == 1) {
result += serialize(children[i]);
// ^^ add a child's result here
}
}
result += "</"+tagname+">";
return result;
}
I would not use trim(), that would produce <div>Hi<b>I</b>am</div> from <div>Hi <b>I</b> am</div>. You might do something like .replace(/\s+/g, " ").
This result+=parse(node); -> In you case you shouldn't merge the result inside recursion like that..
What happens is the return result from <b> recursion call appends the existing result with returned result. Where the existing result is <div>Hi I am and the returned result is <div>Hi I am<b>Test and so at the end of recursion you have <div>Hi I am<div>Hi I am<b>Test.
var result = '';
function parse(htmlStr) {
result += "<" + htmlStr.tagName.toLowerCase() + ">";
var nodes = htmlStr.childNodes;
for (i = 0; i < nodes.length; i++) {
var node = nodes[i];
if (node.nodeType == 3) {
var text = $.trim(node.nodeValue);
if (text !== "") {
result += text;
}
} else if (node.nodeType == 1) {
parse(node);
}
}
console.log(result);
result += "</" + htmlStr.tagName.toLowerCase() + ">";
return result;
}
Fixed fiddle: http://jsfiddle.net/FBnYT/
Change
result+="<"+htmlStr.tagName.toLowerCase()+">";
to:
var result="<"+htmlStr.tagName.toLowerCase()+">";
WOrks fine in demo: http://jsfiddle.net/qtuUA/
The crash occurs because the loop control variable is not locally scoped. So in addition to the other recommended changes:
for (var i = 0; i < nodes.length; i++)
...

Categories