How to get the nth occurrence in a string? - javascript

I would like to get the starting position of the 2nd occurrence of ABC with something like this:
var string = "XYZ 123 ABC 456 ABC 789 ABC";
getPosition(string, 'ABC', 2) // --> 16
How would you do it?

const string = "XYZ 123 ABC 456 ABC 789 ABC";
function getPosition(string, subString, index) {
return string.split(subString, index).join(subString).length;
}
console.log(
getPosition(string, 'ABC', 2) // --> 16
)

You can also use the string indexOf without creating any arrays.
The second parameter is the index to start looking for the next match.
function nthIndex(str, pat, n){
var L= str.length, i= -1;
while(n-- && i++<L){
i= str.indexOf(pat, i);
if (i < 0) break;
}
return i;
}
var s= "XYZ 123 ABC 456 ABC 789 ABC";
nthIndex(s,'ABC',3)
/* returned value: (Number)
24
*/

Working off of kennebec's answer, I created a prototype function which will return -1 if the nth occurence is not found rather than 0.
String.prototype.nthIndexOf = function(pattern, n) {
var i = -1;
while (n-- && i++ < this.length) {
i = this.indexOf(pattern, i);
if (i < 0) break;
}
return i;
}

Because recursion is always the answer.
function getPosition(input, search, nth, curr, cnt) {
curr = curr || 0;
cnt = cnt || 0;
var index = input.indexOf(search);
if (curr === nth) {
if (~index) {
return cnt;
}
else {
return -1;
}
}
else {
if (~index) {
return getPosition(input.slice(index + search.length),
search,
nth,
++curr,
cnt + index + search.length);
}
else {
return -1;
}
}
}

Here's my solution, which just iterates over the string until n matches have been found:
String.prototype.nthIndexOf = function(searchElement, n, fromElement) {
n = n || 0;
fromElement = fromElement || 0;
while (n > 0) {
fromElement = this.indexOf(searchElement, fromElement);
if (fromElement < 0) {
return -1;
}
--n;
++fromElement;
}
return fromElement - 1;
};
var string = "XYZ 123 ABC 456 ABC 789 ABC";
console.log(string.nthIndexOf('ABC', 2));
>> 16

This method creates a function that calls for the index of nth occurrences stored in an array
function nthIndexOf(search, n) {
var myArray = [];
for(var i = 0; i < myString.length; i++) { //loop thru string to check for occurrences
if(myStr.slice(i, i + search.length) === search) { //if match found...
myArray.push(i); //store index of each occurrence
}
}
return myArray[n - 1]; //first occurrence stored in index 0
}

a simple solution just add string, character and idx:
function getCharIdx(str,char,n){
let r = 0
for (let i = 0; i<str.length; i++){
if (str[i] === char){
r++
if (r === n){
return i
}
}
}
}

Shorter way and I think easier, without creating unnecessary strings.
const findNthOccurence = (string, nth, char) => {
let index = 0
for (let i = 0; i < nth; i += 1) {
if (index !== -1) index = string.indexOf(char, index + 1)
}
return index
}

Using indexOf and Recursion:
First check if the nth position passed is greater than the total number of substring occurrences. If passed, recursively go through each index until the nth one is found.
var getNthPosition = function(str, sub, n) {
if (n > str.split(sub).length - 1) return -1;
var recursePosition = function(n) {
if (n === 0) return str.indexOf(sub);
return str.indexOf(sub, recursePosition(n - 1) + 1);
};
return recursePosition(n);
};

function getStringReminder(str, substr, occ) {
let index = str.indexOf(substr);
let preindex = '';
let i = 1;
while (index !== -1) {
preIndex = index;
if (occ == i) {
break;
}
index = str.indexOf(substr, index + 1)
i++;
}
return preIndex;
}
console.log(getStringReminder('bcdefgbcdbcd', 'bcd', 3));

I needed a function that could search from the end of the string too so I wrote this:
function getPos(str, char, index, backwards) {
var split = str.split(char);
var result = 0;
var done = false;
split.forEach(function (item, i) {
if (done) {return}
result += item.length
if (!backwards && i === index) {
done = true
return
} else if (backwards && i === split.length - index - 2) {
done = true
return
}
result += char.length
})
return result
}
Usage:
getPos('x x x', 'x', 1, false) // 2
getPos('x x x', 'x', 0, true) // 4

Using String.indexOf:
var stringToMatch = "XYZ 123 ABC 456 ABC 789 ABC";
function yetAnotherGetNthOccurance(string, seek, occurance) {
var index = 0, i = 1;
while (index !== -1) {
index = string.indexOf(seek, index + 1);
if (occurance === i) {
break;
}
i++;
}
if (index !== -1) {
console.log('Occurance found in ' + index + ' position');
}
else if (index === -1 && i !== occurance) {
console.log('Occurance not found in ' + occurance + ' position');
}
else {
console.log('Occurance not found');
}
}
yetAnotherGetNthOccurance(stringToMatch, 'ABC', 2);
// Output: Occurance found in 16 position
yetAnotherGetNthOccurance(stringToMatch, 'ABC', 20);
// Output: Occurance not found in 20 position
yetAnotherGetNthOccurance(stringToMatch, 'ZAB', 1)
// Output: Occurance not found

var getPosition = function(string, subStr, index) {
if(!string.includes(subStr)) return null;
let arrs = string.split(subStr);
if(arrs.length < index) return null;
let result = 0;
for (let i = 0; i < index; i++) {
result += arrs[i].length;
}
result += (index - 1) * subStr.length;
return result;
}

I was playing around with the following code for another question on StackOverflow and thought that it might be appropriate for here. The function printList2 allows the use of a regex and lists all the occurrences in order. (printList was an attempt at an earlier solution, but it failed in a number of cases.)
<html>
<head>
<title>Checking regex</title>
<script>
var string1 = "123xxx5yyy1234ABCxxxabc";
var search1 = /\d+/;
var search2 = /\d/;
var search3 = /abc/;
function printList(search) {
document.writeln("<p>Searching using regex: " + search + " (printList)</p>");
var list = string1.match(search);
if (list == null) {
document.writeln("<p>No matches</p>");
return;
}
// document.writeln("<p>" + list.toString() + "</p>");
// document.writeln("<p>" + typeof(list1) + "</p>");
// document.writeln("<p>" + Array.isArray(list1) + "</p>");
// document.writeln("<p>" + list1 + "</p>");
var count = list.length;
document.writeln("<ul>");
for (i = 0; i < count; i++) {
document.writeln("<li>" + " " + list[i] + " length=" + list[i].length +
" first position=" + string1.indexOf(list[i]) + "</li>");
}
document.writeln("</ul>");
}
function printList2(search) {
document.writeln("<p>Searching using regex: " + search + " (printList2)</p>");
var index = 0;
var partial = string1;
document.writeln("<ol>");
for (j = 0; j < 100; j++) {
var found = partial.match(search);
if (found == null) {
// document.writeln("<p>not found</p>");
break;
}
var size = found[0].length;
var loc = partial.search(search);
var actloc = loc + index;
document.writeln("<li>" + found[0] + " length=" + size + " first position=" + actloc);
// document.writeln(" " + partial + " " + loc);
partial = partial.substring(loc + size);
index = index + loc + size;
document.writeln("</li>");
}
document.writeln("</ol>");
}
</script>
</head>
<body>
<p>Original string is <script>document.writeln(string1);</script></p>
<script>
printList(/\d+/g);
printList2(/\d+/);
printList(/\d/g);
printList2(/\d/);
printList(/abc/g);
printList2(/abc/);
printList(/ABC/gi);
printList2(/ABC/i);
</script>
</body>
</html>

Related

Run IndexOf more than one time JS

How to search multiple same values and look if it meets the conditions without regex? Suppose that I have this string ***6**5****8*9***2
What I need to do is to check if the string has at least three times *** altogether and then if the number before and after the *** sums 11. In the example given, these conditions are met because: First, the string has three *** altogether, and then 9 + 2 is 11.
I have solved this problem using regex, match, and replace, but how I can do it without applying to those solutions.
I have tried to do this without regex but it did not work because indexOf is only giving me one result:
string = "***6**5****8*9***2";
for (var i = 0; i < string.length; i++) {
let where = (string.indexOf("***"));
let a = parseInt(string.charAt(where - 1));
let b = parseInt(string.charAt(where + 3));
if (a + b == 11){
isTrue = true;
}
}
You could split the string by stars amd keep the starts in the array then check if the value starts with a star, check the length and the left and right element for sum.
var string = "***6**5****8*9***2",
parts = string.split(/(\*+)/),
result = parts.some((s, i, { [i - 1]: l, [i + 1]: r }) =>
s[0] === '*' && s.length >= 3 && +l + +r === 11
);
console.log(result);
console.log(parts);
I tried to create a solution based off of your existing code:
string = "***6**5****8*9***2***4";
let where = 0;
// Skip leading *
while (where < string.length && '*' == string.charAt(where)) {
where += 1;
}
while (true) {
// Find three stars based on previous result
where = string.indexOf("***", where);
// No more triples
if (where == -1) break;
// Convert to digit - will be NaN if not a digit
let a = parseInt(string.charAt(where - 1));
// Find trailing digit
let j = where + 1;
// Skip to next non * char
while (j < string.length && '*' == string.charAt(j)) {
j += 1;
}
// No matches - quit
if (j == string.length) break;
// Parse digit
let b = parseInt(string.charAt(j));
// Do the math
if (!isNaN(a) && !isNaN(b)){
console.log(`${a} + ${b} = ${a+b}`);
}
where = j;
}
I wrote a simple Parser.I hope it can help you
string = "***6**5****8*9***2";
class Parser {
static parse(string) {
var lexer = new Lexer(string);
var token = lexer.getToken();
var prevNumberToken = null ;
while (token.type !== TOKEN_TYPE_END) {
if(token.type === TOKEN_TYPE_NUMBER){
if(prevNumberToken && prevNumberToken.value + token.value === 11 ) {
return true;
}
prevNumberToken = token ;
} else if(token.type !== TOKEN_TYPE_THREESTARS){
prevNumberToken = null ;
}
token = lexer.getToken();
}
return false;
}
}
class Lexer {
constructor(string) {
this._string = string;
this._index = -1;
this._len = string.length;
}
getToken() {
if (this._index < 0) {
this._index = 0;
return new Token(TOKEN_TYPE_START, '', -1);
}
if (this._index >= this._len) {
return new Token(TOKEN_TYPE_END, '', this._len);
}
if (this._string[this._index] === "*") {
return this._getStarToken()
} else {
return this._getNumberToken();
}
}
_getStarToken() {
var stars = "";
var i = this._index;
while (this._index < this._len && this._string[this._index] === "*") {
stars += "*";
this._index++;
}
if (stars.length === 3) {
return new Token(TOKEN_TYPE_THREESTARS, stars, i)
}
return new Token(TOKEN_TYPE_STARS, stars, i)
}
_getNumberToken() {
var numbers = "";
var i = this._index;
while (this._index < this._len && this._string[this._index] !== "*") {
if (this._string[this._index] >= "0" && this._string[this._index] <= "90") {
numbers += this._string[this._index];
this._index++;
} else {
throw new Error("invalid character")
}
}
return new Token(TOKEN_TYPE_NUMBER, parseInt(numbers), i);
}
}
class Token {
constructor(type, value, index) {
this._type = type
this._index = index
this._value = value
}
get type() { return this._type }
get index() { return this._index }
get value() { return this._value }
}
const TOKEN_TYPE_START = 1;
const TOKEN_TYPE_NUMBER = 2;
const TOKEN_TYPE_THREESTARS = 4;
const TOKEN_TYPE_STARS = 8;
const TOKEN_TYPE_END = 16;
console.log(Parser.parse(string));

Reverse the word in a string with the same order in javascript without using the array functions except .length

I want reverse the string with the same order. We should not use the Array functions like split(), .reverse() and .join(). but we can use array.length.
Here i am attached my code. How to achieve that thing more efficiently.
var str = "i am javascript";
// result "i ma tpircsavaj"
function reverseString(myStr){
var strlen = myStr.length, result = "", reverseStr = "", reverseStrArr = [];
for(var i = strlen-1; i >= 0; i--){
reverseStr += myStr[i];
}
for(var j = 0; j < strlen; j++){
if(reverseStr[j] == " "){
reverseStrArr.push(result);
result = "";
}else{
result += reverseStr[j];
if(j + 1 == strlen){
reverseStrArr.push(result);
result = "";
}
}
}
for(var k=reverseStrArr.length - 1; k >= 0; k--){
result += reverseStrArr[k] + " "
}
console.log(result);
}
reverseString(str);
Use 2 pointers: i to indicate the current position and j to indicate the start index of the current word. Add the reversed of current word char by char when space.
Don't be fooled by the nested loops, the complexity is the same as yours: O(n) and somehow cleaner for me.
var string = "i love javascript and the whole world!"
var result = ""
var i = j = 0
var l = string.length
while (i++ < l) {
var k = i
if (string[i] === " " || (i === l - 1) && k++) {
while (--k >= j) result += string[k]
j = i + 1
result += " "
}
}
result = result && result.slice(0, -1) || ""
console.log(result)
You could take a loop, collect the characters of a word and reverse the word.
function reverse(string) {
var reversed = '';
while (reversed.length !== string.length) {
reversed = string[reversed.length] + reversed;
}
return reversed;
}
var string = "i am javascript",
temp = '',
result = '',
i = 0;
while (i < string.length) {
if (string[i] === ' ') {
result += (result && ' ') + reverse(temp);
temp = '';
} else {
temp += string[i];
}
i++;
}
if (temp) result += (result && ' ') + reverse(temp);
console.log(result);
An approach from the end.
function reverse(string) {
var reversed = '';
while (reversed.length !== string.length) {
reversed = string[reversed.length] + reversed;
}
return reversed;
}
var string = "i am javascript",
temp = '',
result = '',
i = string.length;
while (i--) {
if (string[i] === ' ') {
result = ' ' + reverse(temp) + result;
temp = '';
} else {
temp = string[i] + temp;
}
}
if (temp) result = reverse(temp) + result;
console.log(result);
function reverseStr(str) {
var ret = "";
for (var i = 0; i < str.length; i++) {
ret = str[i] + ret;
}
return ret;
}
function doIt(str) {
var ret = "", cur = "";
for (var i = 0; i < str.length; i++) {
var c = str.charAt(i);
if (c == ' ' || c == '.') {
ret += reverseStr(cur) + c;
cur = "";
} else {
cur += c;
}
}
ret += reverseStr(cur);
return ret;
}
console.log(doIt('Reverse the word in a string with the same order in javascript without using the array functions except .length'));
function reverse(word) {
if (word.length > 1) {
var newWord = '';
for (j = word.length-1; j >= 0; j--) {
newWord += word[j];
}
return newWord + ' ';
} else {
return word + ' ';
}
}
var text = "i am javascript";
var words = text.split(' ');
var result = '';
for (i = 0; i < words.length; i++) {
result += reverse(words[i]);
}
console.log(result);

Printing out extra 0's while writing numbers in Expanded Form via javascript

Can't seem to figure out why I keep printing out extra 0's.
As of now, if the value was 730, this is what shows up:
Expected: '700 + 30', instead got: '700 + 30 + 0'
Criteria:
"You will be given a number and you will need to return it as a string in Expanded Form. For example:
12 Should return 10 + 2
42 Should return 40 + 2
70304 Should return 70000 + 300 + 4
NOTE: All numbers will be whole numbers greater than 0."
function expandedForm(num) {
var i,
position,
numArr = Array.from(num.toString()).map(Number),
numArrLen = numArr.length,
result = '';
if(num < 10){
return num;
} else {
for(i = 0; i < numArrLen; i++){
position = numArrLen-1-i;
if( i === numArrLen-1 ){
result += numArr[i] * Math.pow(10, position);
console.log('done',result);
} else {
if(numArr[i] !== 0){
result += numArr[i] * Math.pow(10, position) + " + ";
console.log('keep going',result);
} else {
continue;
console.log('zero', result);
}
}
}
return result;
}
}
Well it goes into the first if check which does not account for zero so you need to check if it is zero in that check.
if (i === numArrLen-1 && numArr[i]!==0)
And the issue you will have after that is you will have a trailing +.
What I would do is instead of adding the string, I would push to an array and join
var result = []
result.push(30)
result.push(2)
console.log(result.join(" + ")
and you can use array methods to actually solve it.
(102030).toString().split("").map((n,i,a) => n*Math.pow(10, a.length-i-1)).filter(n => n>0).join(" + ")
This is how I solved the solution.
function expandedForm(num) {
// Convert num to a string array
let numStringArray = Array.from(String(num));
// Get length of string array
let len = numStringArray.length;
let result = '';
// For each digit in array
numStringArray.map( (n,index) => {
// Perform only if n > 0
if( n>0 ) {
// Add plus sign if result is not empty (for the next digits)
if( result ) { result += ' + '; };
// Pad zeros the right limited to array length minus current index
result += new String(n).padEnd(len-index,'0');
}
});
return result;
}
This was my solution, could have been refactored better but it works.
function expandedForm(num) {
let myStr = num.toString().split(''), //convert number to string array
strLength = myStr.length,
arr = new Array();
if (strLength === 1) { /*If array length is one*/
return num;
}
for (let i = 0; i < strLength; i++) { //loop
if (myStr[i] === 0) { //if number starts with 0
arr.push('0')
}
if (i + 1 === strLength) { //if array is close to end
arr.push(myStr[i])
}
// add zero to number by length difference in array
// add number to
if (myStr[i] !== 0 && i + 1 !== strLength) {
for (let x = 1; x < (strLength - myStr.indexOf(myStr[i])); x++) {
myStr[i] += '0'
}
arr.push(myStr[i]);
}
}
return arr.filter((obj) => obj.match(/[1-9]/)) //remove items with just 0s
.map(obj => obj += ' + ') // add '+'
.reduce((a, b) => a + b).slice(0, -2); //remove whitespace
}
function expandedForm(num) {
const arr = num
.toString()
.split('');
for (let i = 0; i < arr.length - 1; ++i) {
if (arr[i] > 0) {
for (let j = i; j < arr.length - 1; ++j) {
arr[i] += '0';
}
}
}
return arr
.join()
.replace(new RegExp(",0", "g"), "")
.replace(new RegExp(",", "g"), " + ");
}
My solution is fairly similar, uses a bit of RegExp at the end in order to strip out the commas and lone zeroes however

How can i split a string into words without split function in javascript

How can I split string into words without using split function in javascript. I wonder for a little help.
this is my code:
function trocearCadena(cadena) {
var posEspacios = buscarEspacios(cadena);
var palabras = [];
var j = 0;
while (true) {
var pos = posEspacios.shift();
var subcad = (j == 0) ? cadena.substring(0, pos) : cadena.substring(j + 1, pos);
palabras.push(subcad);
j += pos;
if (j > cadena.length) {
var ultpal = cadena.substring(pos + 1);
palabras.push(ultpal);
break;
}
}
return palabras;
}
function buscarEspacios(cadena) {
var espacios = [];
var pos = -1;
do{
pos = cadena.indexOf(" ", ++pos);
if (pos != -1) espacios.push(pos);
} while (pos != -1);
return espacios;
}
I don't understand what your variable names mean, so I wasn't able to fix the code. Here's another one:
str = "How can I split string into words without using split function in javascript."
var words = [""];
for(var i = 0; i < str.length; i++)
if(str[i] !== " ")
words[words.length - 1] += str[i];
else if(words[words.length - 1])
words.push("");
document.write(words)
Assuming that you also can't use what #Oriol suggested, I would use a recursive function like in the following example:
function _split(s, arr) {
var
str = s.trim(),
words = arr || [],
i = str.indexOf(' ');
if(i !== -1) {
words.push(str.substr(0, i)); // collect the next word
return _split(str.slice(i + 1), words); // recur with new string and words array to keep collecting
} else {
words.push(str); // collect the last word
return words;
}
}
Usage:
_split(' one two three ');
//=> ["one", "two", "three"]

Jquery how check if an array string value is in a textarea

var SPECIAL_CHARS = Array("\x5B", "\x5C", "\x5D", "\x5E", "\x7B", "\x7C", "\x7D", "\x7E", 8364, 49792, 14844588, '�', '%', '/', '&','�','!','"','(',')','=','[','\\',']','^','{','|','}','~');
var dynamic_variables = Array("${nome}");
function charUsed(el) {
var base = el;
var count = base.val().length;
var chars = base.val().split("");
var numberOfSpecialChars = 0;
for (var k=0; k<chars.length; k++) {
if ($.inArray(chars[k], SPECIAL_CHARS) > -1) {
numberOfSpecialChars++;
}
}
if($.inArray(base.val(),dynamic_variables) != -1)
{
numberOfSpecialChars = numberOfSpecialChars+40;
}
return count + numberOfSpecialChars;
} // function
Basically, I need to count the textarea length and if this contains some special char (array SPECIAL_CHARS) count X 2 (till here, all goes right).
Now I need to add some other words (no more chars), like ${nome}
Pseudocode:
if an element of array is in base.val(), add 40 to the numberOf SpecialChars
Of course, my code doesn't function.
You can loop through the elements of dynamic_variables Array and check they are present in the main string by using Array.indexOf(). If so add 40.
instead of
if($.inArray(base.val(),dynamic_variables) != -1)
{
numberOfSpecialChars = numberOfSpecialChars+40;
}
do
for(var i=0;i<dynamic_variables.length;i++)
{
if(base.val().indexOf(dynamic_variables[i]) != -1) {
numberOfSpecialChars += 40;
}
}
var SpecialChars = ['\x5B', '\x5C', '\x5D', '\x5E', '\x7B'];
var SpecialWords = ['foo', 'bar'];
function count(text)
{
var result = text.length;
// more effective lookup on big text and special char list
var charLookup = SpecialChars.reduce(function(r, v)
{
r[v] = true;
return r;
}, {});
// count special chars
result += text.split('').reduce(function(r, v)
{
if(v in charLookup)
{
r += 1;
}
return r;
}, 0);
// count special words
result += SpecialWords.reduce(function(r, v)
{
r += (text.split(v).length - 1) * 40;
return r;
}, 0);
return result;
}
var result = count(document.getElementById('text').value);
console.assert(result == 'bar some } text [ bar'.length + 2 * 1 + 2 * 40);
document.getElementById('result').innerHTML = result;
<textarea id='text'>bar some { text [ bar</textarea>
<div>Result: <span id='result'></span></div>

Categories