Javascript and regex for replace text - javascript

Sir,
I was doing a project with javascript .I want to replace a text with regex.in a paragraph i want to replace some word using javascript
eg:
var str =" Would you like to have responses to your questions |code Would you like to have responses to your questions code| Would you like to have responses to your questions "
var n=str.replace("to","2");
Here all the "to" will be replaced.We don't want to remove between |code to code| Please help me any one.....

I think you shouldn't rely on regexps for these kind of tasks, since it would be overcomplicated and therefore slow. Anyway, if your expression is correct (i.e., for every "|code" there's a "code|" and there aren't nested code tags), you can try this:
var n = str.replace(/to(?!(?:\|(?!code)|[^\|])*code\|)/g, "2");
Not only it's complicated, but it's hard to maintain. The best thing to do in these cases is to split your string to chunks:
var chunks = [], i, p = 0, q = 0;
while ((p = str.indexOf("|code", p)) !== -1) {
if (q < p) chunks.push(str.substring(q, p));
q = str.indexOf("code|", p);
chunks.push(str.substring(p, p = q = q + 5));
}
if (q < str.length) chunks.push(str.substring(q));
// chunks === [" Would you like to have responses to your questions ",
// "|code Would you like to have responses to your questions code|",
// " Would you like to have responses to your questions "]
Note: str.replace("to", "2") does not replace every occurrence of "to", but only the first one.

if you want to replace all 'to' into 2 this will be the code
var str =" Would you like to have responses to your questions |code Would you like to have responses to your questions code| Would you like to have responses to your questions";
var n=str.replace(/to/g,"2");

Related

Passing a string through an array looking for regEx

I'm currently making a chatbox in JQuery. I've been using indexOf but I think it might be more efficient to use regExp.
my current code is
function ai(message){
if (username.length<3){
username = message;
send_message("Nice, to meet you " + username + ", how are you doing?");
}
if(message.indexOf("how are you?")>=0) {
send_message("I'm feeling great!");
}
if(message.indexOf("weather")>=0 ){
send_message("In England it is shitty");
}
var n = message.search(/\b(cat|cats|kitten|feline)\b/i);
if (n !== -1) {
send_message("i hate cats");
}
else {
for (i=0; i <= botChat.length; i++) {
var re = new RegExp (botChat[i][0], 'i');
if (re.test(message)) {
var length = botChat[i].length - 1;
var index = Math.ceil( length * Math.random());
var reply = (botChat[i][index]);
send_message(reply);
}
}
}
}
and a typical line from my array is
new Array ("I need (.*)\." , "Why do you need $1?", "Would it really help you to get $1?" , "Are you sure you need $1?"),
i'm trying to demonstrate the ways of creating a chatbot. The first four responses work perfectly
it takes a name, comments on the weather and can search for cats. What it can't do is perform the loop. Has anyone any suggestions?

Regex efficiency, better way to select text among html

This is about a Chrome Extension.
Suppose a user select any text on a page, then clicks a button to save it. Via window.getSelection() I can get that text without the underlying html markup.
I store that text. For demo purposes, let's say the text is:
"John was much more likely to buy if he knew the price beforehand"
The next time the user visits the page, I want to find that text on the page. The issue is, the html for that text is actually:
<b>John was much more likely to buy if he knew the price <span class="italic">beforehand</span></b>
The second issue is that this system needs to work even if the selection is dirty, i.e. it starts/ends mid DOM node.
What I've build is bit of a fat solution, so I am curious how I can make it more efficient and/or smaller. This is the whole thing:
text.split("").map(function(el, i, arr){
if(specials.includes(el)){
return "\\"+el;
}
return el;
})
.join("(?:\\s*<[^>]+>\\s*)*\\s*");
where text is the saved text and specials is
var specials = [
'/', '.', '*', '+', '?', '|',
'(', ')', '[', ']', '{', '}', '\\'
];
The process is:
Split text into single characters
For each character, check if it's a special char and if so, prepend it with \
Join all letters together with regEx that check if there's any whitespace or html tags inbetween
My question is, can it be done in a better way? I get the "bruteforcing" feeling with this solution and I don't know if it would actually cause lag on larger sites/selection texts.
Plus, it doesn't work for SPAs where text may update a bit after the DOM is ready.
Thank you for any input.
EDIT:
So initially I was using mark.js, which doesn't handle this at all, but not 12 hours after I posted this question the maintainer release v8.0.0 that uses NodeList and handles my use case. The feature is "acrossElements", located here.
create a Range object
set it so that it spans the entire document from start to end
check if the string of interest is in its toString()
clone range twice
apply binary search by moving the start/end points of the subranges into roughly their midpoint. this can be approximated by finding the first descendant with > 1 child nodes and then splitting the child list
goto 3
this should roughly take n log m steps where n is the document text length and m the number of nodes.
Build the entire text representation of the document manually from each node with nodeType of Node.TEXT_NODE, saving the node reference and its text's start/end positions relative to the overall string in an array. Do it just once as DOM is slow, and you might want to search for multiple strings. Otherwise the other answer might be much faster (without actual benchmarks it's a moot point).
Apply HTML whitespace coalescing rules.
Otherwise you'll end up with huge spans of spaces and newline characters.
For example, Range.toString() doesn't strip them, meaning you'd have to convert your string to a RegExp with [\s\n\r]+ instead of spaces and all other special characters like {}()[]|^$*.?+ escaped.
Anyway, it'd be wise to use the converted RegExp on document.body.textContent before proceeding (easy to implement, many examples on the net, thus not included below).
A simplified implementation for plain-string search follows.
function TextMap(baseElement) {
this.baseElement = baseElement || document.body;
var textArray = [], textNodes = [], textLen = 0, collapseSpace = true;
var walker = document.createTreeWalker(this.baseElement, NodeFilter.SHOW_TEXT);
while (walker.nextNode()) {
var node = walker.currentNode;
var nodeText = node.textContent;
var parentName = node.parentNode.localName;
if (parentName==='noscript' || parentName==='script' || parentName==='style') {
continue;
}
if (parentName==='textarea' || parentName==='pre') {
nodeText = nodeText.replace(/^(\r\n|[\r\n])/, '');
collapseSpace = false;
} else {
nodeText = nodeText.replace(/^[\s\r\n]+/, collapseSpace ? '' : ' ')
.replace(/[\s\r\n]+$/, ' ');
collapseSpace = nodeText.endsWith(' ');
}
if (nodeText) {
var len = nodeText.length;
textArray.push(nodeText);
textNodes.push({
node: node,
start: textLen,
end: textLen + len - 1,
});
textLen += len;
}
}
this.text = textArray.join('');
this.nodeMap = textNodes;
}
TextMap.prototype.indexOf = function(str) {
var pos = this.text.indexOf(str);
if (pos < 0) {
return [];
}
var index1 = this.bisectLeft(pos);
var index2 = this.bisectRight(pos + str.length - 1, index1);
return this.nodeMap.slice(index1, index2 + 1)
.map(function(info) { return info.node });
}
TextMap.prototype.bisect =
TextMap.prototype.bisectLeft = function(pos) {
var a = 0, b = this.nodeMap.length - 1;
while (a < b - 1) {
var c = (a + b) / 2 |0;
if (this.nodeMap[c].start > pos) {
b = c;
} else {
a = c;
}
}
return this.nodeMap[b].start > pos ? a : b;
}
TextMap.prototype.bisectRight = function(pos, startIndex) {
var a = startIndex |0, b = this.nodeMap.length - 1;
while (a < b - 1) {
var c = (a + b) / 2 |0;
if (this.nodeMap[c].end > pos) {
b = c;
} else {
a = c;
}
}
return this.nodeMap[a].end >= pos ? a : b;
}
Usage:
var textNodes = new TextMap().indexOf('<span class="italic">');
When executed on this question's page:
[text, text, text, text, text, text]
Those are text nodes, so to access corresponding DOM elements use the standard .parentNode:
var textElements = textNodes.map(function(n) { return n.parentNode });
Array[6]
  0: span.tag
  1: span.pln
  2: span.atn
  3: span.pun
  4: span.atv
  5: span.tag

Letters to predefined numbers conversion in one window

)
I have searched high and low, but i can´t find what i need. Or i´m to stupid to get it right ;-)
I need a page with several input boxes where i can type some text, and then an output area below each input, that shows the text converted to some predefined numbers.
example:
input: abcde fghi æøå (i need all kinds of characters like .,/: etc.)
output: 064 065 066 067 068 032
So it needs to convert like this:
"a"="064 "
"b"="065 "
"space"="032 "
(and yes, each number in output needs to be separated, or a space added after each number)
I have tried some different cipher guides in both php and javascript, but can´t get it to work. I did do an Excel document that could do some of it, but it had a limited amount of characters it could convert, then it started behaving weird. So i thought maybe PHP was the answer!
Any help is very appreciated
/Rasmus
In the spirit of elclanrs deleted answer, and for posterity:
<script>
// Using standard for loop
function stringToCharcodes(s) {
var result = [];
function pad(n){ return (n<10? '00' : n<100? '0' : 0) + n;}
for (var i=0, iLen=s.length; i<iLen; i++) {
result.push(pad(s.charCodeAt(i)));
}
return result.join(' ');
}
// Using ES5 forEach
function stringToCharcodes2(s) {
var result = [];
function pad(n){ return (n<10? '00' : n<100? '0' : 0) + n;}
s.split('').forEach(function(a){result.push(pad(a.charCodeAt(0)))});
return result.join(' ');
}
</script>
<input onkeyup="document.getElementById('s0').innerHTML = stringToCharcodes(this.value);"><br>
<span id="s0"></span>
Edit
If you want a custom mapping, use an object (I've only included 2 characters, you can add as many as you want):
var mapChars = (function() {
var mapping = {'198':'019', '230':'018'};
return function (s) {
var c, result = [];
for (var i=0, iLen=s.length; i<iLen; i++) {
c = s.charCodeAt(i);
result.push(c in mapping? mapping[c] : c);
}
return result.join(' ');
}
}());
alert(mapChars('Ææ')); //
Using the character code for mapping seems to be a reasonable solution, using the actual character may be subject to different page character encoding.

Javascript for Variations with Repetition (combinatorics) of missing string characters

My question is similar to THIS question that hasn't been answered yet.
How can I make my code (or any javascript code that might be suggested?) find all possible solutions of a known string length with multiple missing characters in variation with repetition?
I'm trying to take a string of known character lengths and find missing characters from that string. For example:
var missing_string = "ov!rf!ow"; //where "!" are the missing characters
I'm hoping to run a script with a specific array such as:
var r = new Array("A","B","C","D","E","F","G","H","I","J","K",
"L","M","N","O","P","Q","R","S","T","U","V",
"W","X","Y","Z",0,1,2,3,4,5,6,7,8,9);
To find all the possible variations with repetition of those missing characters to get a result of:
ovArfAow
ovBrfAow
ovCrfAow
...
ovBrfBow
ovBrfCow
...
etc //ignore the case insensitive, just to emphasize the example
and of course, eventually find ovErfLow within all the variations with repetition.
I've been able to make it work with 1 (single) missing character. However, when I put 2 missing characters with my code it obviously repeats the same array character for both missing characters which is GREAT for repition but I also need to find without repetition as well and might need to have 3-4 missing characters as well which may or may not be repeated. Here's what I have so far:
var r = new Array("A","B","C","D","E","F","G","H","I","J","K",
"L","M","N","O","P","Q","R","S","T","U","V",
"W","X","Y","Z",0,1,2,3,4,5,6,7,8,9);
var missing_string = "he!!ow!r!d";
var bt_lng = missing_string.length;
var bruted="";
for (z=0; z<r.length; z++) {
for(var x=0;x<bt_lng;x++){
for(var y=0;y<r.length;y++){
if(missing_string.charAt(x) == "!"){
bruted += r[z];
break;
}
else if(missing_string.charAt(x) == r[y]){
bruted += r[y];
}
}
}
console.log("br: " + bruted);
bruted="";
}
This works GREAT with just ONE "!":
helloworAd
helloworBd
helloworCd
...
helloworLd
However with 2 or more "!", I get:
heAAowArAd
heBBowBrBd
heCCowCrCd
...
heLLowLrLd
which is good for the repetition part but I also need to test all possible array M characters in each missing character spot.
Maybe the following function in pure javascript is a possible solution for you. It uses Array.prototype.reduce to create the cartesian product c of the given alphabet x, whereby its power n depends on the count of the exclamation marks in your word w.
function combinations(w) {
var x = new Array(
"A","B","C","D","E","F","G","H","I","J","K",
"L","M","N","O","P","Q","R","S","T","U","V",
"W","X","Y","Z",0,1,2,3,4,5,6,7,8,9
),
n = w.match(/\!/g).length,
x_n = new Array(),
r = new Array(),
c = null;
for (var i = n; i > 0; i--) {
x_n.push(x);
}
c = x_n.reduce(function(a, b) {
var c = [];
a.forEach(function(a) {
b.forEach(function(b) {
c.push(a.concat([b]));
});
});
return c;
}, [[]]);
for (var i = 0, j = 0; i < c.length; i++, j = 0) {
r.push(w.replace(/\!/g, function(s, k) {
return c[i][j++];
}));
}
return r;
}
Call it like this console.log(combinations("ov!rf!ow")) in your browser console.

Fastest way to search string in javascript

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.

Categories