I have the following code which detects which search engine and what search term has been used:
if (document.referrer.search(/google\.*/i) != -1) {
var start = document.referrer.search(/q=/);
var searchTerms = document.referrer.substring(start + 2);
var end = searchTerms.search(/&/);
end = (end == -1) ? searchTerms.length : end;
searchTerms = searchTerms.substring(0, end);
if (searchTerms.length != 0) {
searchTerms = searchTerms.replace(/\+/g, " ");
searchTerms = unescape(searchTerms);
alert('You have searched: '+searchTerms+' on google');
}
}
That actually works, but unfortunately it doesn't work as expected sometimes.
Sometimes if the referrer was even not google i get an alert with the search term as : ttp://www.domain.com ( without H at the start ) i think that may lead to the bug.
Appreciate any help!
Have you tried leveraging existing JS URL parsing schemes? It might save you a bunch of time. For example:
http://blog.stevenlevithan.com/archives/parseuri
It's cutting the "h" off because q= was not in the referrer string. So your start variable is -1. Then you add 2 to that to get your searchTerms var with a substring. You need to check for start to be equal to -1 and return.
I also think your "google" string detection is not bulletproof, I would rather do something like this...
var ref = document.referrer;
var pcol = ref.indexOf("://") + 3;
if(ref.indexOf("google.com") == pcol || ref.indexOf("www.google.com") == pcol) {
// It is google
}
One last thing, you should use decodeURIComponent instead of unescape.
Related
hosts=".uk.com:hostname:#10.10.10.10/10:#[2001:db8:1/64]:#11.11.11.11/11:#[::2/24]"
In javascript, how do i split the above string("hosts") string like the following :
newhosts=.uk.com,hostname,#10.10.10.10/10,#[2001:db8:1/64],#11.11.11.11/11,#[::2/24]"
tried this :
var hosts, newhosts;
var ip6_hosts = [];
var ip6_re = /#\[(.*?)\]/g;
hosts=".uk.com:hostname:#10.10.10.10/10:#[2001:db8:1/64]:#11.11.11.11/11:#[::2/24]";
while ((match=ip6_re.exec(hosts)) != null)
ip6_hosts.push(match[0]);
non_ip6_hosts=hosts.replace(ip6_re, '').replace(/:+/g, ':');
newhosts=ip6_hosts.concat(non_ip6_hosts.split(':'));
actual output :
newhosts=#[2001:db8:1/64],#[::2/24],.uk.com,hostname,#10.10.10.10/10,#11.11.11.11/11
expected output :
newhosts=.uk.com,hostname,#10.10.10.10/10,#[2001:db8:1/64],#11.11.11.11/11,#[::2/24]
but not sure how to preserve the order. is there any way to achieve an expected output ?
You could try:
var openbracket=0;
for (i=0; i<hosts.length; i++)
{
if (hosts.substr(i,1) == '[') openbracket=openbracket+1;
if (hosts.substr(i,1) == ']') openbracket=openbracket-1;
if ((hosts.substr(i,1) == ':') && openbracket==0)
{
hosts = hosts.substr(0,i) + ',' + hosts.substr(i+1,hosts.length-i-1);
}
}
seems to work for me, though I'm not sure if there's a better method for changing the value of hosts. All it needs to do is insert the ',' at the location i. The above code adds everything to the left of the ':', a ',', and everything to the right of the ':'.
note: this assumes you don't want any ':' inside of brackets changed to a comma.
hope this helps.
Can't You just say:
host = host.replace(/:+/, ',');
whenever you want to change it?
I feel like this is too simple of an answer, comment if I'm not getting it.
The following should work:
hosts.replace(/([^:]{1})\:{1}([^:]{1})/g, '$1,$2')
Try this.
var hosts='.uk.com:hostname:#10.10.10.10/10:#[2001:db8:1/64]:#11.11.11.11/11:#[::2/24]';
hosts = hosts.replace(/:#/g, ':##');
hosts = hosts.split(':#');
var hostDetails = hosts[0].split(':');
var newHost = hostDetails.concat(hosts.splice(1, hosts.length));
console.log(newHost);
Can you try this...
String.prototype.replaceAt=function(index, character) {
return this.substr(0, index) + character + this.substr(index+character.length);
}
hosts=".uk.com:hostname:#10.10.10.10/10:#[2001:db8:1/64]:#11.11.11.11/11:#[::2/24]"
hosts = hosts.split(':#').join(',#');
var re = /:\w/g;
var found = hosts.match(re);
hosts.replaceAt(found.index,',');
I'm writing a script that's going to take some information about the website that you visit. I have copied this small portion of my code that I'm struggling with. This part of the code is supposed check if the visited website is using the www prefix and remove that prefix, then there is another part of the code that I haven't pasted stores the domain name in the variable website.
var website = location.hostname;
document.getElementById("displayBefore").innerHTML = website; //test to see the variable
if (website[0] == 'w' && website[1] == 'w' && website[2] == 'w' && website[3] == '.') {
document.getElementById("displayTrue1").innerHTML = "true"; //test to see if the conditional was met
for (i = 4; i < website.length; i++) {
website[i - 4] = website[i]; //this is not rewriting anything
document.getElementById("displayPos0").innerHTML = website[i]; //test to see if the for loop has run
}
document.getElementById("displayDuring").innerHTML = website; //test to see the variable
website.splice(0, 4); //this is breaking everything after it
document.getElementById("displayAfter").innerHTML = website; //test to see the variable
}
Here is what's actually being displayed when in those tests when I pull it up in a browser:
WebsiteBeforeFix: www.example.com
True1: true
website[i]: m
WebsiteDuringFix: www.example.com
WebsiteAfterFix:
The two parts of the code that aren't working are the following:
website[i - 4] = website[i];
This is supposed to pretty much shift the letters over 4 spaces to the left(eliminating "www.").
website.splice(0,4);
This is actually causing nothing after it to display at all in any of the code that does work. Can anyone tell me what I may be doing wrong?
splice is an array method, not for strings (they're immutable). Make the variable an array to manipulate it using the split method, and join it back together using the join method:
var websiteStr = location.hostname;
var website = websiteStr.split('');
console.log("displayBefore: " + website.join(''));
if (websiteStr.indexOf("www.") === 0) {
console.log("true");
/*for (var i = 4; i < website.length; i++) {
website[i - 4] = website[i];
console.log("displayPos0: " + website[i]);
}*/
console.log("displayDuring: " + website.join(''));
website.splice(0, 4);
console.log("displayAfter: " + website.join(''));
}
Instead of manipulating HTML, you can use console.log to do basic logging at particular points, which will show up in your browser's console. Anyway, it seems that your for loop doesn't do what you want it to -- splice already removes the "www." prefix.
You can also change this:
if (website[0] == 'w' && website[1] == 'w' && website[2] == 'w' && website[3] == '.') {
to this:
if (websiteStr.indexOf("www.") === 0) {
which performs the same thing much more concisely.
With the fixed code, it now displays:
displayBefore: www.google.com
true
displayDuring: www.google.com
displayAfter: google.com
I'm programming my own autocomplete textbox control using C# and javascript on clientside. On client side i want to replace the characters in string which matching the characters the user was searching for to highlight it. For example if the user was searching for the characters 'bue' i want to replace this letters in the word 'marbuel' like so:
mar<span style="color:#81BEF7;font-weight:bold">bue</span>l
in order to give the matching part another color. This works pretty fine if i have 100-200 items in my autocomplete, but when it comes to 500 or more, it takes too mutch time.
The following code shows my method which does the logic for this:
HighlightTextPart: function (text, part) {
var currentPartIndex = 0;
var partLength = part.length;
var finalString = '';
var highlightPart = '';
var bFoundPart = false;
var bFoundPartHandled = false;
var charToAdd;
for (var i = 0; i < text.length; i++) {
var myChar = text[i];
charToAdd = null;
if (!bFoundPart) {
var myCharLower = myChar.toLowerCase();
var charToCompare = part[currentPartIndex].toLowerCase();
if (charToCompare == myCharLower) {
highlightPart += myChar;
if (currentPartIndex == partLength - 1)
bFoundPart = true;
currentPartIndex++;
}
else {
currentPartIndex = 0;
highlightPart = '';
charToAdd = myChar;
}
}
else
charToAdd = myChar;
if (bFoundPart && !bFoundPartHandled) {
finalString += '<span style="color:#81BEF7;font-weight:bold">' + highlightPart + '</span>';
bFoundPartHandled = true;
}
if (charToAdd != null)
finalString += charToAdd;
}
return finalString;
},
This method only highlight the first occurence of the matching part.
I use it as follows. Once the request is coming back from server i build an html UL list with the matching items by looping over each item and in each loop i call this method in order to highlight the matching part.
As i told for up to 100 items it woks pretty nice but it is too mutch for 500 or more.
Is there any way to make it faster? Maybe by using regex or some other technique?
I also thought about using "setTimeOut" to do it in a extra function or maybe do it only for the items, which currently are visible, because only a couple of items are visible while for the others you have to scroll.
Try limiting visible list size, so you are only showing 100 items at maximum for example. From a usability standpoint, perhaps even go down to only 20 items, so it would be even faster than that. Also consider using classes - see if it improves performance. So instead of
mar<span style="color:#81BEF7;font-weight:bold">bue</span>l
You will have this:
mar<span class="highlight">bue</span>l
String replacement in JavaScript is pretty easy with String.replace():
function linkify(s, part)
{
return s.replace(part, function(m) {
return '<span style="color:#81BEF7;font-weight:bold">' + htmlspecialchars(m) + '</span>';
});
}
function htmlspecialchars(txt)
{
return txt.replace('<', '<')
.replace('>', '>')
.replace('"', '"')
.replace('&', '&');
}
console.log(linkify('marbuel', 'bue'));
I fixed this problem by using regex instead of my method posted previous. I replace the string now with the following code:
return text.replace(new RegExp('(' + part + ')', 'gi'), "<span>$1</span>");
This is pretty fast. Much faster as the code above. 500 items in the autocomplete seems to be no problem. But can anybody explain, why this is so mutch faster as my method or doing it with string.replace without regex? I have no idea.
Thx!
I have a hidden field on my page that stores space separated list of emails.
I can have maximum 500 emails in that field.
What will be the fastest way to search if a given email already exists in that list?
I need to search multiple emails in a loop
use RegEx to find a match
use indexOf()
convert the list to a
javascript dictionary and then
search
If this is an exact duplicate, please let me know the other question.
Thanks
EDIT:
Thanks everyone for your valuable comments and answers.
Basically my user has a list of emails(0-500) in db.
User is presented with his own contact list.
User can then choose one\more emails from his contact list to add to the list.
I want to ensure at client side that he is not adding duplicate emails.
Whole operation is driven by ajax, so jsvascript is required.
The answer is: It depends.
It depends on what you actually want to measure.
It depends on the relationship between how many you're searching for vs. how many you're searching.
It depends on the JavaScript implementation. Different implementations usually have radically different performance characteristics. This is one of the many reasons why the rule "Don't optimize prematurely" applies especially to cross-implementation JavaScript.
...but provided you're looking for a lot fewer than you have in total, it's probably String#indexOf unless you can create the dictionary once and reuse it (not just this one loop of looking for X entries, but every loop looking for X entries, which I tend to doubt is your use-case), in which case that's hands-down faster to build the 500-key dictionary and use that.
I put together a test case on jsperf comparing the results of looking for five strings buried in a string containing 500 space-delimited, unique entries. Note that that jsperf page compares some apples and oranges (cases where we can ignore setup and what kind of setup we're ignoring), but jsperf was being a pain about splitting it and I decided to leave that as an exercise for the reader.
In my tests of what I actually think you're doing, Chrome, Firefox, IE6, IE7 and IE9 did String#indexOf fastest. Opera did RegExp alternation fastest. (Note that IE6 and IE7 don't have Array#indexOf; the others do.) If you can ignore dictionary setup time, then using a dictionary is the hands-down winner.
Here's the prep code:
// ==== Main Setup
var toFind = ["aaaaa100#zzzzz", "aaaaa200#zzzzz", "aaaaa300#zzzzz", "aaaaa400#zzzzz", "aaaaa500#zzzzz"];
var theString = (function() {
var m, n;
m = [];
for (n = 1; n <= 500; ++n) {
m.push("aaaaa" + n + "#zzzzz");
}
return m.join(" ");
})();
// ==== String#indexOf (and RegExp) setup for when we can ignore setup
var preppedString = " " + theString + " ";
// ==== RegExp setup for test case ignoring RegExp setup time
var theRegExp = new RegExp(" (?:" + toFind.join("|") + ") ", "g");
// ==== Dictionary setup for test case ignoring Dictionary setup time
var theDictionary = (function() {
var dict = {};
var index;
var values = theString.split(" ");
for (index = 0; index < values.length; ++index) {
dict[values[index]] = true;
}
return dict;
})();
// ==== Array setup time for test cases where we ignore array setup time
var theArray = theString.split(" ");
The String#indexOf test:
var index;
for (index = 0; index < toFind.length; ++index) {
if (theString.indexOf(toFind[index]) < 0) {
throw "Error";
}
}
The String#indexOf (ignore setup) test, in which we ignore the (small) overhead of putting spaces at either end of the big string:
var index;
for (index = 0; index < toFind.length; ++index) {
if (preppedString.indexOf(toFind[index]) < 0) {
throw "Error";
}
}
The RegExp alternation test:
// Note: In real life, you'd have to escape the values from toFind
// to make sure they didn't have special regexp chars in them
var regexp = new RegExp(" (?:" + toFind.join("|") + ") ", "g");
var match, counter = 0;
var str = " " + theString + " ";
for (match = regexp.exec(str); match; match = regexp.exec(str)) {
++counter;
}
if (counter != 5) {
throw "Error";
}
The RegExp alternation (ignore setup) test, where we ignore the time it takes to set up the RegExp object and putting spaces at either end of the big string (I don't think this applies to your situation, the addresses you're looking for would be static):
var match, counter = 0;
for (match = theRegExp.exec(preppedString); match; match = theRegExp.exec(preppedString)) {
++counter;
}
if (counter != 5) {
throw "Error";
}
The Dictionary test:
var dict = {};
var index;
var values = theString.split(" ");
for (index = 0; index < values.length; ++index) {
dict[values[index]] = true;
}
for (index = 0; index < toFind.length; ++index) {
if (!(toFind[index] in dict)) {
throw "Error";
}
}
The Dictionary (ignore setup) test, where we don't worry about the setup time for the dictionary; note that this is different than the RegExp alternation (ignore setup) test because it assumes the overall list is invariant:
var index;
for (index = 0; index < toFind.length; ++index) {
if (!(toFind[index] in theDictionary)) {
throw "Error";
}
}
The Array#indexOf test (note that some very old implementations of JavaScript may not have Array#indexOf):
var values = theString.split(" ");
var index;
for (index = 0; index < toFind.length; ++index) {
if (values.indexOf(toFind[index]) < 0) {
throw "Error";
}
}
The Array#indexOf (ignore setup) test, which like Dictionary (ignore setup) assumes the overall list is invariant:
var index;
for (index = 0; index < toFind.length; ++index) {
if (theArray.indexOf(toFind[index]) < 0) {
throw "Error";
}
}
Instead of looking for the fastest solution, you first need to make sure that you’re actually having a correct solution. Because there are four cases an e-mail address can appear and a naive search can fail:
Alone: user#example.com
At the begin: user#example.com ...
At the end: ... user#example.com
In between: ... user#example.com ...
Now let’s analyze each variant:
To allow arbitrary input, you will need to escape the input properly. You can use the following method to do so:
RegExp.quote = function(str) {
return str.toString().replace(/(?=[.?*+^$[\]\\(){}-])/g, "\\");
};
To match all four cases, you can use the following pattern:
/(?:^|\ )user#example\.com(?![^\ ])/
Thus:
var inList = new RegExp("(?:^| )" + RegExp.quote(needle) + "(?![^ ])").test(haystack);
Using indexOf is a little more complex as you need to check the boundaries manually:
var pos = haystack.indexOf(needle);
if (pos != -1 && (pos != 0 && haystack.charAt(pos-1) !== " " || haystack.length < (pos+needle.length) && haystack.charAt(pos+needle.length) !== " ")) {
pos = -1;
}
var inList = pos != -1;
This one is rather quite simple:
var dict = {};
haystack.match(/[^\ ]+/g).map(function(match) { dict[match] = true; });
var inList = dict.hasOwnProperty(haystack);
Now to test what variant is the fastest, you can do that at jsPerf.
indexOf() is most probably the fastest just keep in mind you need to search for two possible cases:
var existingEmails = "email1, email2, ...";
var newEmail = "somethingHere#email.com";
var exists = (existingEmails.indexOf(newEmail + " ") >= 0) || (existingEmails.indexOf(" " + newEmail ) > 0);
You're asking a question with too many unstated variables for us to answer. For example, how many times do you expect to perform this search? only once? A hundred times? Is this a fixed list of emails, or does it change every time? Are you loading the emails with the page, or by AJAX?
IF you are performing more than one search, or the emails are loaded with the page, then you are probably best off creating a dictionary of the names, and using the Javascript in operator.
If you get the string from some off-page source, and you only search it once, then indexOf may well be better.
In all cases, if you really care about the speed, you're best off making a test.
But then I'd ask "Why do you care about the speed?" This is a web page, where loading the page happens at network speeds; the search happens at more or less local-processor speed. It's very unlikely that this one search will make a perceptible difference in the behavior of the page.
Here is a little explanation:
Performing a dictionary lookup is relatively complicated - very fast compared with (say) a linear lookup by key when there are lots of keys, but much more complicated than a straight array lookup. It has to calculate the hash of the key, then work out which bucket that should be in, possibly deal with duplicate hashes (or duplicate buckets) and then check for equality.
As always, choose the right data structure for the job - and if you really can get away with just indexing into an array (or List) then yes, that will be blindingly fast.
The above has been taken from one of the blog posts of #Jon Skeet.
I know this is an old question, but here goes an answer for those who might need in the future.
I made some tests and the indexOf() method is impossibly fast!
Tested the case on Opera 12.16 and it took 216µs to search and possibly find something.
Here is the code used:
console.time('a');
var a=((Math.random()*1e8)>>0).toString(16);
for(var i=0;i<1000;++i)a=a+' '+((Math.random()*1e8)>>0).toString(16)+((Math.random()*1e8)>>0).toString(16)+((Math.random()*1e8)>>0).toString(16)+((Math.random()*1e8)>>0).toString(16);
console.timeEnd('a');
console.time('b');
var b=(' '+a).indexOf(((Math.random()*1e8)>>0).toString(16));
console.timeEnd('b');
console.log([a,b]);
In the console you will see a huge output.
The timer 'a' counts the time taken to make the "garbage", and the timer 'b' is the time to search for the string.
Just adding 2 spaces, one before and one after, on the email list and adding 1 space before and after the email, you are set to go.
I use it to search for a class in an element without jQuery and it works pretty fast and fine.
I recently created a function in javascript that takes in a file name and a max character limit where the result needs to follow these rules:
Always include file extension
If shrinking occurs, leave the first part and last part of the file name intact.
Always replace the removed characters with '...'
If file length is under the max then do nothing
You can assume the max is a least 5 chars long
Now I've already solved this, but it got me thinking if there is a more elegant or simple way to do this in javascript using regular expressions or some other technique. It also gave me an opportunity to try out jsFiddle. So with that in mind here is my function:
function ReduceFileName(name, max){
if(name.length > max){
var end = name.substring(name.lastIndexOf('.'));
var begin = name.substring(0, name.lastIndexOf('.'));
max = max - end.length - 3;
begin = begin.substr(0,max/2) + '...' + begin.substr(begin.length-(max/2) , max/2 + 1);
return begin + end;
}
return name;
}
And here it is on js Fiddle with tests
I'm not sure that regular expressions will be necessarily more elegant, but so far I came up with the following which passes your tests:
function ReduceFileName(name, max){
if(name.length > max) {
var ell ="\u2026"; // defines replacement characters
var ext = (/\.[^\.]*$/.exec(name) || [""])[0]; // gets extension (with dot) or "" if no dot
var m = (max-ell.length-ext.length)/2; // splits the remaining # of characters
var a = Math.ceil(m);
var z = Math.floor(m);
var regex = new RegExp("^(.{"+a+"}).*(.{"+z+"})"+ext, "");
var ret = regex.exec(name);
return ret[1]+ell+ret[2]+ext;
}
return name;
}
Since I didn't get much activity on this, I'm assuming there isn't a much better way to do this, so I'll consider my method as the answer until someone else comes up with something else.
function ReduceFileName(name, max){
if(name.length > max){
var end = name.substring(name.lastIndexOf('.'));
var begin = name.substring(0, name.lastIndexOf('.'));
max = max - end.length - 3;
begin = begin.substr(0,max/2) + '...' + begin.substr(begin.length-(max/2) , max/2 + 1);
return begin + end;
}
return name;
}