I'm following the excersises in the book Eloquent Javascript and came up to this piece of code:
function between(string, start, end)
{
// Start AT the last character position of the start string
var startAt = string.indexOf(start) + start.length;
//Count the position of the end string first character
var endAt = string.indexOf(end, startAt);
return string.slice(startAt, endAt);
}
var betweenIn = between('Inazuma Eleven', 'Ina', 'ven');
console.log(betweenIn);
Code works fine. It extracts a piece of string between in. Now I tried to really understand this piece, but one thing isn't clear to me. The variable endAt checks the string's first character position of the third given parameter, (in this case my string is 'Inazuma Eleven' end the parameter is 'ven'. I need that for slicing the string, BUT it seems that the second parameter of the indexOf method doesn't do anything. If I remove it, I get the same results. Why is this?
The second parameter of indexOf defaults to 0. This is the place in the string where it will start looking for your matching substring.
Starting after the end of the start string ensures that a) your end string doesn't match the first instance of it if the start string and end string are identical, and b) you have to scan less of the target string so the code runs faster.
In this instance, your start and end strings are different, so the outcome is the same. However since the indexOf method will be searching more of the string (starting from 0 instead of the 4th character) it will run fractionally slower.
Related
i'm a new one in coding and learning the indexOf() method i found such part of code (see below)
I wonder why do we use the if() part in the code below. Why doesn't it work without it?
var myString = 'Welcome to Wrox books. ';
myString = myString + 'The Wrox website is www.wrox.com. ';
myString = myString + 'Visit the Wrox website today. Thanks for buying Wrox';
var foundAtPosition = 0;
var wroxCount = 0;
while (foundAtPosition != -1)
{
foundAtPosition = myString.indexOf('Wrox',foundAtPosition);
if (foundAtPosition != -1)
{
wroxCount++;
foundAtPosition++;
}
}
document.write('There are ' + wroxCount + ' occurrences of the word Wrox');
Will appreciate any thoughts.
You need the if to check if you have found the string you are looking for.
indexOf() will return -1 if the string is not found
The indexOf() method accepts two arguments: the substring to find in the given string, and the location(index) at which it should start searching for the substring. It returns the location(index) of the first occurrence of the substring(if the second argument is provided, it will be the first one after the provided location).
So, what the code inside the loop does:
Finds the location of the first occurrence of the substring using the indexOf() method and saves the location in the variable foundAtPosition.
If the substring was found(foundAtPosition!=-1), the wroxCount variable is incremented to indicate the number of substrings found so far, and the foundAtPosition variable is incremented as well(read on about it).
The while loop checks the condition, and if it is satisfied it executes its contents again.
The loop finds the location of the first occurrence of the substring, but now it starts searching at foundAtPosition. As the foundAtPosition variable was incremented by one, it will start searching after the previously found occurrence, and will return the location of the next one.
This goes on until there are no more occurrences left, which leaves the wroxCount variable containing the number of the occurrences of the substring in the main string.
The result: you get the number of times the word(substring) you are looking for occurs in the string myString.
You're counting the position of wrox everytime you found it. If you do not have more occourences, it won't increment the counter wroxCount because we don't want the counter to count after the foundAtPosition becomes -1. Or in other words, after last occourence, or if wrox is not found at all.
I have a value - toggle-save_2
I want to extract the 2 part. The part after the underscore will always be what i need but the length of the former part, which in this case is toggle-save_, may vary (eg. notoggle-val_45). Though this length may vary it will always be separated from the end number by an underscore.
Right now I am using this
var current = this.id.split('_');
current = current[1];
to select the number.
What would be cool is if I could pass a variable to the split to only give me the second index of the result from the split.
Just select the 2nd index when you do the split.
var current = this.id.split('_')[1];
The best solution here would be to use lastIndexOf and substring, like this
function getLastPart(strObject) {
return strObject.substring(strObject.lastIndexOf("_") + 1);
}
console.log(getLastPart("toggle-save_2"));
// 2
console.log(getLastPart("notoggle-save_45"));
// 45
It is better for this case because, you already know that the _ will be somewhere near the last position. Since lastIndexOf starts from the last position, it would find _ very soon and all we need to do is to get the rest of the string from the next position.
There are often times I am breaking up a string where I only need the very last value of the result of String.prototype.split and consider the rest to be garbage no matter how many values the split produced.
When those cases arise, I like to chain Array.prototype.pop off of the split
var s = 'toggle-save_2',
current = s.split('_').pop();
The split method can only be limited from the end, and it will always return an array.
You don't need to use split, you can use string operations to get part of the string:
var current = this.id.substr(this.id.indexOf('_') + 1);
I have a string like this:
The planet is very big and the planet is heavy.
Using Javascript, I would like to search for a string starting from the end and stopping at another string.
An example would be, start at the word heavy, search for the word planet, but stop at the word big. The second instance of planet in this example is what I want, not the first. Therefore if there is not an instance of planet between big and heavy then I want to yield an indexOf -1.
Thanks.
Edit: I can accomplish this by splitting my string on the word I want than searching the second element of the array for my text but I was wondering if there was a function to do this.
You could split on the word "big" and then only lastIndexOf from the last subsstring group.
IE:
function foo(input,search,stopWord){
//Split the input
var arr=input.split(stopWord);
//return only from last substring group
return arr[arr.length-1].lastIndexOf(search);
}
foo("The planet is very big and the planet is heavy.","planet","big");
Does that give you the result you expect?
Run
var startIdx = string.lastIndexOf('big');
var grabIdx = string.lastIndexOf('planet');
(startIdx < grabIdx)?grabIdx: -1)
I'm defining a regex object and then matching it in a loop. It only matches sometimes, to be precise - every second time. So I created a smallest working sample of this problem.
I tried this code in Opera and Firefox. The behavior is the same in both:
>>> domainRegex = /(?:\.|^)([a-z0-9\-]+\.[a-z0-9\-]+)$/g;
/(?:\.|^)([a-z0-9\-]+\.[a-z0-9\-]+)$/g
>>> domainRegex.exec('mail-we0-f174.google.com');
Array [".google.com", "google.com"]
>>> domainRegex.exec('mail-we0-f174.google.com');
null
>>> domainRegex.exec('mail-we0-f174.google.com');
Array [".google.com", "google.com"]
>>> domainRegex.exec('mail-we0-f174.google.com');
null
>>> domainRegex.exec('mail-we0-f174.google.com');
Array [".google.com", "google.com"]
>>> domainRegex.exec('mail-we0-f174.google.com');
null
Why is this happening? Is this behaviour documented? Is there a way around, other than defining the regex inside loop body?
exec() works in the manner you have described; with the /g modifier present, it will return a match, starting from lastIndex with every invocation until there are no more matches, at which point it returns null and the value of lastIndex is reset to 0.
However, because you have anchored the expression using $ there won't be more than one match, so you can use String.match() instead and lose the /g modifier:
var domainRegex = /(?:\.|^)([a-z0-9\-]+\.[a-z0-9\-]+)$/;
'mail-we0-f174.google.com'.match(domainRegex); // [".google.com", "google.com"]
Additional Info to Ja͢cks response:
You can also set lastIndex
var myRgx = /test/g;
myRgx.exec(someString);
myRgx.lastIndex = 0;
or just create a new regex for each execution, which i find even cleaner
new RegExp(myRgx).exec(someString);
When performing a global search with a RegExp, the exec method starts matching beginning at
the lastIndex property. The lastIndex property is set at each exec invocation and
is set to the position following the last match found. If a match fails, lastIndex is reset to 0, which causes exec to match from the start again.
var a = 'asdfeeeasdfeedxasdf'
undefined
var p = /asdf/g
p.lastIndex
4
p.exec(a)
["asdf"]
p.lastIndex
11
p.exec(a)
["asdf"]
p.lastIndex
19
p.exec(a)
null //match failed
p.lastIndex
0 //lastIndex reset. next match will start at the beginning of the string a
p.exec(a)
["asdf"]
Each time you run the exec method of your regex it gets you the next match.
Once it reaches the end of the string, it returns null to let you know you've got all of the matches. The next time, it starts again from the begining.
As you only have one match (which returns an array of the full match and the match from the brackets), The first time, the regex starts searching from the start. It finds a match and returns it. The next time, it gets to the end and returns null. So if you had this in a loop, you could do something like this to loop through all matches:
while(regExpression.exec(string)){
// do something
}
Then the next time, it starts again from position 0.
"Is there a way around?"
Well, if you know there's only one match, or you only want the first match, you can save te result to a variable. There's no need to resuse .exec. If you are interested in all the matches, then you need to keep going until you get null.
why don't you use simple match method for string like
'mail-we0-f174.google.com'.match(/(?:\.|^)([a-z0-9\-]+\.[a-z0-9\-]+)$/)
First of all, pardon my hacky code, I'm just trying to try this out and I'm learning javascript for the first time. Basically, Given the string "abc!random{3}" would mean return a string that starts with "abc" and ends with a random number from 0-3.
Here is what I have:
var pattern=/!random{([^{]*?)}/gi;
var text="abc!random{3}def!random{4}ghi!random{!random{3}}";
while (pattern.test(text))
{
text=text.replace(pattern, Math.random() * parseInt("$1"));
}
The problem is the parseInt function. It seems like the $1 does not get passed to it..the value of it gets cleared or something. If I do:
text=text.replace(pattern, "$1");
It correctly returns what is in between the { }, so the regex is working and the match is being stored in $1. However, as soon as I use it as a parameter to $1, it seems like the value of it is cleared. What gives?
The second parameter to replace can either be a string to replace the entire match, within which, if present, all occurances of $1, $2, etc. are replaced with the captures OR it can be a function that takes two parameters (the match and the capture) and returns a replacement string for the capture:
var pattern=/!random{([^{]*?)}/gi;
var text="abc!random{3}def!random{4}ghi!random{!random{3}}";
text=text.replace(pattern,
function(match,capture)
{
return Math.random() * parseInt(capture)
});
WScript.echo(text);
Notice that the while loop is unnecessary: the regular expression already has the global flag set ("g"), which says to process the regex globally.
Also note that the function actually receives multiple (m) arguments: 1=the matched string, 2..m=the captures from left to right, m+2=the offset within the string where the match occured, and m+3=the entire string being matched. JavaScript allows the right-most arguments (of any function) to be omitted, which is why the example has only two.
The replace method is operating on the value that it's passed. That is, if it gets a string containing $0, $1, etc., then it replaces that with the correct match. However, what you wrote is first parsing the string "$1" as an integer, multiplying it by a random number, and then passing that as the replacement. So it's almost certainly passing it NaN (due to being unable to parse "$1"), which is not what you want.
So, instead of
text = text.replace(pattern, Math.random() * parseInt("$1"));
I would try
var matches = text.match(pattern);
text = text.replace(pattern, Math.random() * parseInt(matches[1]));