Related
Currently it is not possible in confluence to have the headings of the document structure numbered automatically. I am aware that there are (paid) 3rd party plugins available.
How can I achieve continuous numbered headings?
TL;DR
Create a bookmark for the following javascript and click it in edit mode in confluence to renumber your headings.
javascript:(function()%7Bfunction%20addIndex()%20%7Bvar%20indices%20%3D%20%5B%5D%3BjQuery(%22.ak-editor-content-area%20.ProseMirror%22).find(%22h1%2Ch2%2Ch3%2Ch4%2Ch5%2Ch6%22).each(function(i%2Ce)%20%7Bvar%20hIndex%20%3D%20parseInt(this.nodeName.substring(1))%20-%201%3Bif%20(indices.length%20-%201%20%3E%20hIndex)%20%7Bindices%3D%20indices.slice(0%2C%20hIndex%20%2B%201%20)%3B%7Dif%20(indices%5BhIndex%5D%20%3D%3D%20undefined)%20%7Bindices%5BhIndex%5D%20%3D%200%3B%7Dindices%5BhIndex%5D%2B%2B%3BjQuery(this).html(indices.join(%22.%22)%2B%22.%20%22%20%2B%20removeNo(jQuery(this).html()))%3B%7D)%3B%7Dfunction%20removeNo(str)%20%7Blet%20newstr%20%3D%20str.trim()%3Bnewstr%20%3D%20newstr.replace(%2F%5B%5Cu00A0%5Cu1680%E2%80%8B%5Cu180e%5Cu2000-%5Cu2009%5Cu200a%E2%80%8B%5Cu200b%E2%80%8B%5Cu202f%5Cu205f%E2%80%8B%5Cu3000%5D%2Fg%2C'%20')%3Bif(IsNumeric(newstr.substring(0%2Cnewstr.indexOf('%20'))))%7Breturn%20newstr.substring(newstr.indexOf('%20')%2B1).trim()%3B%7Dreturn%20newstr%3B%7Dfunction%20IsNumeric(num)%20%7Bnum%20%3D%20num.split('.').join(%22%22)%3Breturn%20(num%20%3E%3D0%20%7C%7C%20num%20%3C%200)%3B%7DaddIndex()%7D)()
Result
How to use
After changes to the structure have been made, clicking the bookmarked javascript renumbers the document.
Limitations are that it only provides n.n.n. numbering, but for many cases that's sufficient. The script can also be customized as required.
Background, explanation and disclosure
I tried this TaperMonkey script that apparently resulted from this post, but it didn't work as is. So I took its source code and stripped it of the integration code, old version compatibility and made some minor adjustments to get this:
function addIndex() {
var indices = [];
jQuery(".ak-editor-content-area .ProseMirror").find("h1,h2,h3,h4,h5,h6").each(function(i,e) {
var hIndex = parseInt(this.nodeName.substring(1)) - 1;
if (indices.length - 1 > hIndex) {
indices= indices.slice(0, hIndex + 1 );
}
if (indices[hIndex] == undefined) {
indices[hIndex] = 0;
}
indices[hIndex]++;
jQuery(this).html(indices.join(".")+". " + removeNo(jQuery(this).html()));
});
}
function removeNo(str) {
let newstr = str.trim();
newstr = newstr.replace(/[\u00A0\u1680\u180e\u2000-\u2009\u200a\u200b\u202f\u205f\u3000]/g,' ');
if(IsNumeric(newstr.substring(0,newstr.indexOf(' ')))){
return newstr.substring(newstr.indexOf(' ')+1).trim();
}
return newstr;
}
function IsNumeric(num) {
num = num.split('.').join("");
return (num >=0 || num < 0);
}
addIndex();
(I'm not a JavaScript developer, I'm sure it can be written nicer/better)
Then I used bookmarklet to convert it into the javascript bookmark at the top, which can be clicked to trigger the functionality.
A online tool, such as JSCompress, will reduce code size up to 80%. It's easy to notice that the result, compressed code, removes space. Beyond the removal of EOL and ' ' characters, is there any other trickery needed to minify a js file?
Example compressed:
function glow(e){$("#"+e).fadeIn(700,function(){$(this).fadeOut(700)})}function startLevel(){ptrn=[],pos=0,setLevel(lvl),$("#mg-lvl").fadeOut("slow",function(){$("#mg-contain").prop("onclick",null).off("click"),$("#mg-contain").css("cursor","default"),$(this).text("Level "+lvl+": "+ptrn.length+" blink(s)."),$(this).fadeIn("slow"),showLevel(0)})}function setLevel(e){ptrn.push(Math.floor(3*Math.random()+1)),0==e||setLevel(--e)}function showLevel(e){$("#b"+ptrn[e]+"c").fadeOut(speed,function(){$("#ball_"+ptrn[e]).fadeOut(speed,function(){$("#b"+ptrn[e]+"c").fadeIn(speed),$(this).fadeIn(speed,function(){e+1<ptrn.length&&showLevel(++e,speed)})})}),e+1==ptrn.length&&setTimeout(bindKeys(1),ptrn.length*speed+15)}function bindKeys(e){for(var e=1;e<4;e++)bind(e)}function bind(e){$("#ball_"+e).on("click",function(){$("#b"+e+"c").fadeOut(speed,function(){$("#ball_"+e).fadeOut(speed,function(){$("#ball_"+e).fadeIn(speed),$("#b"+e+"c").fadeIn(speed),referee(e)&&unbind()})})})}function referee(e){if(pos<ptrn.length&&(e===ptrn[pos]?$("#mg-score").text(parseInt($("#mg-score").text())+1):end()),++pos==ptrn.length)return++lvl,speed-=40,!0}function unbind(){for(var e=1;e<4;e++)$("#ball_"+e).off();startLevel()}function nestedFade(e,n,t){e[n]&&$(e[n]).fadeOut("fast",function(){t[n]&&($(e),t[n]),nestedFade(e,++n,t)})}function end(){for(var e=[],n=[],t=1;t<4;t++)e.push("#b"+t+"c"),e.push("#ball_"+t),n.push(null);e.push("#mg-contain"),n.push('.fadeOut("slow")'),e.push("#mg-obj"),n.push(".fadeOut('slow')"),e.push("#bg-ball-container"),n.push(".toggle()"),nestedFade(e,0,n)}var ptrn=[],pos=0,lvl=1,speed=400,b1=setInterval(function(){glow("ball_1b",700)}),b2=setInterval(function(){glow("ball_2b",700)}),b3=setInterval(function(){glow("ball_3b",700)});
Example uncompressed:
var ptrn = [];
var pos = 0;
var lvl = 1;
var speed = 400;
/* make balls glow */
function glow(id)
{
$('#'+id).fadeIn(700, function(){$(this).fadeOut(700);})
}
var b1 = setInterval(function(){ glow('ball_1b',700) ,1500});
var b2 = setInterval(function(){ glow('ball_2b',700) ,1500});
var b3 = setInterval(function(){ glow('ball_3b',700) ,1500});
/* end */
function startLevel()
{
ptrn = [];
pos = 0;
/* set pattern for the level */
setLevel(lvl);
/* display prompt for level */
$('#mg-lvl').fadeOut("slow", function(){
$('#mg-contain').prop('onclick',null).off('click');
$('#mg-contain').css('cursor','default');
$(this).text("Level " + lvl + ": " + ptrn.length + " blink(s).");
$(this).fadeIn('slow');
/* play back the pattern for user to play */
showLevel(0); //TODO: use promise and deferred pattern to pull this out of fade function.
});
}
function setLevel(lvl)
{
ptrn.push(Math.floor((Math.random() * 3) + 1));
(lvl == 0 ) ? null : setLevel(--lvl);
}
function showLevel(i)
{
/* blink the balls */
$('#b'+ptrn[i]+'c').fadeOut(speed, function(){
$('#ball_'+ptrn[i]).fadeOut(speed, function(){
$('#b'+ptrn[i]+'c').fadeIn(speed);
$(this).fadeIn(speed, function(){
if(i+1<ptrn.length)
showLevel(++i,speed);
});
});
});
if( (i+1) == ptrn.length)
setTimeout( bindKeys(1), ptrn.length*speed+15) //after the pattern is revealed bind the clicker
}
function bindKeys(i)
{
for(var i=1;i<4;i++)
bind(i);
}
function bind(i)
{
$('#ball_'+i).on('click', function() {
$('#b'+i+'c').fadeOut(speed, function() {
$('#ball_'+i).fadeOut(speed, function() {
$('#ball_'+i).fadeIn(speed);
$('#b'+i+'c').fadeIn(speed);
if(referee(i))
unbind();
});
});
});
}
function referee(val)
{
if(pos < ptrn.length){
( val === ptrn[pos] ) ? $('#mg-score').text(parseInt($('#mg-score').text())+1) : end();
}
if(++pos == ptrn.length)
{
++lvl;
speed-=40;
return true;
}
}
function unbind()
{
for(var i=1;i<4;i++)
$( "#ball_"+i).off();
startLevel();
}
function nestedFade(id,i,func)
{
(!id[i]) ? 0 : $(id[i]).fadeOut('fast',function(){ if(func[i])
{$(id)+func[i];};nestedFade(id,++i,func);})
}
function end()
{
var id = [];
var func = [];
for(var i=1;i<4;i++){
id.push('#b'+i+'c');
id.push('#ball_'+i);
func.push(null)
}
id.push('#mg-contain');
func.push('.fadeOut("slow")');
id.push('#mg-obj');
func.push(".fadeOut('slow')");
id.push('#bg-ball-container');
func.push(".toggle()");
nestedFade(id,0,func);
}
Saves 32% on file size...and if that is the case, is it a fair assumption then that writing less is doing more for the end user?
The same way you can 'minify' a file to reduce its size, you can also 'uglify' a file, which takes your code and shortens things like variable names to the same end: reduce file size by reducing the number of characters in it (not just removing line breaks and space characters).
While it will reduce loadtime for a user, it's not a great practice to write minified/uglified-style code off the bat. That's why in almost any professional build/deploy process, you take your clear, descriptive code and then run your build processes to reduce the size of your files and eventually deploy versions that your end user will have a quicker time loading. You can always write your regular code, then run a compression process like the one you described, save it into a "public" folder and upload that for users to have access to, rather than your fleshed out code.
All a minifier will do is remove white space, which like you said, is ' ' and EOL characters. I believe you may be thinking of file compression tools such as a .zip file with the way your question is worded. Such file types (.zip) will find common strings in your file, and put references to the original string rather than having it written out 10 times. Meaning if the string "I like cake" shows up 4 times in your file, it will have "I like cake" in one location, and the other three locations will reference that first location, shortening the length of the file and therefore decreasing its size.
Well the main reason JS, CSS and HTML get's minified is to decrease the size of the files transmitted from server to client when a client requests a webpage. This decrease in size will allow for a faster load time. So technically writing less is more for a webpages load time, but realistically the effect of you as a developer consciously writing shorter code to minimize file size will either a.) Be to minimal a change to actually make a difference or b.) lead to loss of functionality or bugs due to the focus being on cutting down code length, not code quality.
i am new to Google apps script, i want to create string of random characters in the code given below in variable body2.
function myfunction() {
var files = DriveApp.getFiles();
while (files.hasNext(`enter code here`)) {
Logger.log(files.next().getName());
}
var recipient = Session.getActiveUser().getEmail();
var subject = 'A list of files in your Google Drive';
var body1 = Logger.getLog();
var body2;
for(var i=0;i<6;i++)
{
body2[i]=BigNumber.tostring("Math.floor(Math.random()*11)");
}
body=body1+body2;
MailApp.sendEmail(recipient, subject, body);
};
but when i run this function, it says "TypeError: Cannot find function tostring in object 0. (line 12, file "Code") " i can't understand how to solve this error?
Why we have to multiply random by 11 , can it be multiplied with any integer number?
what if i want that string in only capital letters.!
Some other question
1) i don't have enough knowledge of JavaScript, is it good to learn GAS directly?
2) i can't find proper written material or documentation for GAS , the material available at Google's official site is seems to be updating time by time , what to do then ? any link to material would help me .!
I guess I just figured
function randomStr(m) {
var m = m || 15; s = '', r = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (var i=0; i < m; i++) { s += r.charAt(Math.floor(Math.random()*r.length)); }
return s;
};
Hope someone finds it helpful.
As for a random string use this its better:
Math.random().toString(36). 36 is the base thus will use letters and numbers in the string.
As for gas documentation, the official page is pretty complete. It changes because it constantly improves and adds new services.
I have this charIdGeneration() in my GAS library
function charIdGenerator()
{
var charId ="";
for (var i = 1; i < 10 ; i++)
{
charId += String.fromCharCode(97 + Math.random()*10);
}
//Logger.log(charId)
return charId;
}
I want to display YouTube videos on my website, but I need to be able to add a unique id for each video that's going to be shared by users. So I put this together, and I have run into a little problem. I am trying to get the JavaScript to add a random string for the div id, but it's not working, showing the string:
<script type='text/javascript' src='jwplayer.js'></script>
<script type='text/javascript'>
function randomString(length) {
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'.split('');
if (! length) {
length = Math.floor(Math.random() * chars.length);
}
var str = '';
for (var i = 0; i < length; i++) {
str += chars[Math.floor(Math.random() * chars.length)];
}
return str;
}
var div = randomString(8);
</script>
<div id='div()'>This text will be replaced</div>
<script type='text/javascript'>
jwplayer('div()').setup({
'flashplayer': 'player.swf',
'file': 'http://www.youtube.com/watch?v=4AX0bi9GXXY',
'controlbar': 'bottom',
'width': '470',
'height': '320'
});
</script>
I really like this function:
function guidGenerator() {
var S4 = function() {
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
};
return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
}
From Create GUID / UUID in JavaScript?
2018 edit: I think this answer has some interesting info, but for any practical applications you should use Joe's answer instead.
A simple way to create a unique ID in JavaScript is to use the Date object:
var uniqid = Date.now();
That gives you the total milliseconds elapsed since January 1st 1970, which is a unique value every time you call that.
The problem with that value now is that you cannot use it as an element's ID, since in HTML, IDs need to start with an alphabetical character. There is also the problem that two users doing an action at the exact same time might result in the same ID. We could lessen the probability of that, and fix our alphabetical character problem, by appending a random letter before the numerical part of the ID.
var randLetter = String.fromCharCode(65 + Math.floor(Math.random() * 26));
var uniqid = randLetter + Date.now();
This still has a chance, however slim, of colliding though. Your best bet for a unique id is to keep a running count, increment it every time, and do all that in a single place, ie, on the server.
Here is the reusable function to generate the random IDs :
function revisedRandId() {
return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(2, 10);
}
// It will not start with the any number digit so it will be supported by CSS3
I think some folks here haven't really focused on your particular question. It looks like the problem you have is in putting the random number in the page and hooking the player up to it. There are a number of ways to do that. The simplest is with a small change to your existing code like this to document.write() the result into the page. I wouldn't normally recommend document.write(), but since your code is already inline and what you were trying do already was to put the div inline, this is the simplest way to do that. At the point where you have the random number, you just use this to put it and the div into the page:
var randomId = "x" + randomString(8);
document.write('<div id="' + randomId + '">This text will be replaced</div>');
and then, you refer to that in the jwplayer set up code like this:
jwplayer(randomId).setup({
And the whole block of code would look like this:
<script type='text/javascript' src='jwplayer.js'></script>
<script type='text/javascript'>
function randomString(length) {
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghiklmnopqrstuvwxyz'.split('');
if (! length) {
length = Math.floor(Math.random() * chars.length);
}
var str = '';
for (var i = 0; i < length; i++) {
str += chars[Math.floor(Math.random() * chars.length)];
}
return str;
}
var randomId = "x" + randomString(8);
document.write('<div id="' + randomId + '">This text will be replaced</div>');
jwplayer(randomId).setup({
'flashplayer': 'player.swf',
'file': 'http://www.youtube.com/watch?v=4AX0bi9GXXY',
'controlbar': 'bottom',
'width': '470',
'height': '320'
});
</script>
Another way to do it
I might add here at the end that generating a truly random number just to create a unique div ID is way overkill. You don't need a random number. You just need an ID that won't otherwise exist in the page. Frameworks like YUI have such a function and all they do is have a global variable that gets incremented each time the function is called and then combine that with a unique base string. It can look something like this:
var generateID = (function() {
var globalIdCounter = 0;
return function(baseStr) {
return(baseStr + globalIdCounter++);
}
})();
And, then in practical use, you would do something like this:
var randomId = generateID("myMovieContainer"); // "myMovieContainer1"
document.write('<div id="' + randomId + '">This text will be replaced</div>');
jwplayer(randomId).setup({
i like this simple one:
function randstr(prefix)
{
return Math.random().toString(36).replace('0.',prefix || '');
}
since id should (though not must) start with a letter, i'd use it like this:
let div_id = randstr('youtube_div_');
some example values:
youtube_div_4vvbgs01076
youtube_div_1rofi36hslx
youtube_div_i62wtpptnpo
youtube_div_rl4fc05xahs
youtube_div_jb9bu85go7
youtube_div_etmk8u7a3r9
youtube_div_7jrzty7x4ft
youtube_div_f41t3hxrxy
youtube_div_8822fmp5sc8
youtube_div_bv3a3flv425
I also needed a random id, I went with using base64 encoding:
btoa(Math.random()).substring(0,12)
Pick however many characters you want, the result is usually at least 24 characters.
Based on HTML 4, the id should start from letter:
ID and NAME tokens must begin with a letter ([A-Za-z]) and may be followed by any number of letters, digits ([0-9]), hyphens ("-"), underscores ("_"), colons (":"), and periods (".").
So, one of the solutions could be (alphanumeric):
var length = 9;
var prefix = 'my-awesome-prefix-'; // To be 100% sure id starts with letter
// Convert it to base 36 (numbers + letters), and grab the first 9 characters
// after the decimal.
var id = prefix + Math.random().toString(36).substr(2, length);
Another solution - generate string with letters only:
var length = 9;
var id = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, length);
Or you could use Cripto since it's already built in(except in IE11, I swear these guys havent updated in years!)
https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues#Examples
var id = new Uint32Array(10);
window.crypto.getRandomValues(id);
I also found this:
https://gist.github.com/6174/6062387#gistcomment-3255605
let length = 32;
let id = crypto.randomBytes(length).toString("base64");
There's a lot of ways to do this, but for most people, there's no reason to reinvent the wheel :)
A edited version of #jfriend000 version:
/**
* Generates a random string
*
* #param int length_
* #return string
*/
function randomString(length_) {
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghiklmnopqrstuvwxyz'.split('');
if (typeof length_ !== "number") {
length_ = Math.floor(Math.random() * chars.length_);
}
var str = '';
for (var i = 0; i < length_; i++) {
str += chars[Math.floor(Math.random() * chars.length)];
}
return str;
}
For generating random ids, you can also use the standard crypto API with its randomUUID() function which is available in node.js (>=v16.7.0) and all relevant browsers except Safari:
const uuid = crypto.randomUUID()
console.log(uuid)
// prints e.g. "7f3f4512-fcf9-45fe-b726-512bba403426"
I would suggest that you start with some sort of placeholder, you may have this already, but its somewhere to append the div.
<div id="placeholder"></div>
Now, the idea is to dynamically create a new div, with your random id:
var rndId = randomString(8);
var div = document.createElement('div');
div.id = rndId
div.innerHTML = "Whatever you want the content of your div to be";
this can be apended to your placeholder as follows:
document.getElementById('placeholder').appendChild(div);
You can then use that in your jwplayer code:
jwplayer(rndId).setup(...);
Live example: http://jsfiddle.net/pNYZp/
Sidenote: Im pretty sure id's must start with an alpha character (ie, no numbers) - you might want to change your implementation of randomstring to enforce this rule. (ref)
May I an share an intuitive way to generate a randomID ?
const getRandomID = (length: number) => {
let text = '';
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (let i = 0; i < length; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}
Here is an easy one liner:
const generateUniqueID = (idLength) => [...Array(idLength).keys()].map((elem)=>Math.random().toString(36).substr(2, 1)).join("")
Where all you do is enter the idLength and it will return a unique id of that length.
generateUniqueID(23)
>>>'s3y9uebzuo73ih79g0s9p2q' // Id of length 23
First. Assign an id to your div. Like this:
<div id="uniqueid">This text will be replaced</div>
After that, add inside your <script> tag following code:
Document.getElementById("uniqueid").id = randomString(8);
window.btoa(String.fromCharCode(...window.crypto.getRandomValues(new Uint8Array(5))))
Using characters except ASCII letters, digits, '_', '-' and '.' may cause compatibility problems, as they weren't allowed in HTML 4. Though this restriction has been lifted in HTML5, an ID should start with a letter for compatibility.
function id(prefix = '', length = 7) {
let result = prefix;
for(let i = 0; i < length; i++) {
const random = Math.random();
result += String.fromCharCode(Math.floor(random * 26) + (random < .5 ? 65 : 97));
}
return result;
}
a random number between 0 and 25 is generated then added to either 65 or 97. When added to 65 it will give you an ascii code for a capital letter and when added to 97, an ascii code for a small letter.
Just use built-int crypto.randomUUID() which is supportted by all major browsers:
let uuid = crypto.randomUUID();
console.log(uuid);
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.