I want to match the word hello that is not between any type of quote ('' or "" or ``) and not in parentheses.
An example match would be:
'hello'(hello)('hello')hello
^^^^^ # the last hello is matched
If there is any way to do something like this I would appreciate it. Thanks!
What i have tried is this:
'(hello)+(?=(?:[^\'"`]*[\'"`][^\'"`]*[\'"`])*[^\'"`]*$)(?!\()(?<!\))'
but it always returns nothing as a match
I am trying to get the index of the match.
A regular expression is not really the right tool for this. Moreover, JavaScript does not (at this moment) support (?R)-recursion logic, so for supporting nested parentheses detection, it would be better to use a plain old for loop.
Here is a function that does the job. It is run with the example input and word you have provided and it returns the index at which it finds a good match -- in this case 23:
function findOccurrence(input, word) {
for (let i = 0, j = -1, nesting = 0; i < input.length; i++) {
let ch = input[i];
if (i > j) {
j = input.indexOf(word, i); // find next occurrence of word
if (j < 0) return -1; // no more occurrences...
}
if (i === j && nesting === 0) return i; // bingo!
if ("`'\"".includes(ch)) { // opening quote
i = input.indexOf(ch, i+1); // jump to closing quote (of same type)
} else if (ch === "(") {
nesting++;
} else if (nesting > 0 && ch === ")") {
nesting--;
}
}
return -1; // not found
}
// example:
console.log(findOccurrence("'hello'(hello)('hello')hello", "hello"));
Related
My code works and I need help figuring out how to optimize it. If possible, don't give me any code, just tips on optimizing it, please.
The rules for the puzzle are:
Each triplet has the rules of how the letters are ordered in the secret word (each letter is followed by the next letter inside the triplet array).
all the letters of the secret word are distinct.
Example input:
triplets1 = [
['t','u','p'],
['w','h','i'],
['t','s','u'],
['a','t','s'],
['h','a','p'],
['t','i','s'],
['w','h','s']
]
Expected Output: "whatisup"
I came up with this code, which works. It selects the first letters that are not preceded by any others in the arrays they are placed, removes them, concatenates them into the final word, and keep doing that until all the arrays are empty. The code isn't being accepted though, because is exceeding the time limit.
function recoverSecret(triplets) {
let secretWord = '', character = '';
let notEmpty = true;
let size = triplets.length;
//it loops until array is empty
while(notEmpty) {
notEmpty = false;
for (let i = 0; i < size; i++) {
for (let j = 0; j < triplets[i].length; j++) {
if (character) j = 0; //everytime a character is included, this condition is truthy, so you have to go back to the start of the array because the character was removed last iteration
character = triplets[i][j];
let remove = []; //this array will have the positions of the letter to remove in the removal cycle
for (let k = 0; k < size; k++) {
if (character == triplets[k][0]) remove.push(k);
//if the letter is in the triplet and it's not the first position, then it isn't the letter we're looking for, so character equals to '', otherwise it will be the letter which will be added to the secretWord string
if (k != i && (triplets[k].includes(character))) {
if (character != triplets[k][0]) {
character = '';
break;
}
}
}
secretWord += character;
if (character) {
//if character is not '', then a removal loop is done to remove the letter because we just found its place
for (x of remove) {
triplets[x].shift();
}
}
// if (triplets[i].length == 0) break;
}
if (triplets[i] != 0) notEmpty = true; //if every triplet is empty, notEmpty remains false and while loop is over
}
}
return secretWord;
}
If possible I don't want any code, just tips on optimization, please.
After some thought, I got it right. The most important change was realizing that I didn't have to loop through each char in the array. I only needed to do it for the first char of each triplet sequence.
Then, made some other adjustments, like, instead of two ifs for checking if the char is the first element of every triplet it's in, I used the condition triplets[k].indexOf(character) > 0 to check both things at the same time, since indexOf method returns -1 if it doesn't find what it is looking for.
I'm not saying it's the best it could be, but now it is a little better in performance than it was
function recoverSecret(triplets) {
let secretWord = '', character = '';
let notEmpty = true;
while (notEmpty) {
notEmpty = false;
for (let i = 0; i < triplets.length; i++) {
if (triplets[i].length == 0) continue;
character = triplets[i][0];
let remove = [];
for (let k = 0; k < triplets.length; k++) {
if (triplets[k].indexOf(character) > 0) {
character = '';
break;
}
else if (triplets[k][0] == character) remove.push(k);
}
secretWord += character;
if (character) {
for (x of remove) {
triplets[x].shift();
}
}
if (triplets[i].length != 0) { notEmpty = true; }
console.log(triplets)
}
return secretWord;
}
}
Guys i'm trying to write an algorithm where I pass in a large string and let it loop through the string and whatever palindrome it finds, it pushes into array but for some reason my browser is crashing once i put in the while loop and I have no
function arrOfPalindromes(str) {
var palindromeArrays = []
var plength = palindromeArrays.length
// grab first character
// put that in a temp
// continue and look for match
// when match found go up one from temp and down one from index of loop
// if matched continue
// else escape and carry on
// if palendrome push into array
var counter = 0;
for (var i = 0; i < str.length; i++) {
for (var j = 1; j < str.length - 1; j++) {
if (str[i + counter] === str[j - counter]) {
while (str[i + counter] === str[j - counter]) {
console.log(str[j], str[i])
// append the letter to the last index of the array
palindromeArrays[plength] += str[i]
counter++
}
}
}
}
return palindromeArrays
}
var result2 = arrOfPalindromes('asdfmadamasdfbigccbigsdf')
console.log(result2)
Do not mention about the algorithm but the condition
while (str[i + counter] === str[j - counter])
Make your code crash. A little surprise but str[j+counter] when j+counter > str.length return undefined and the same as j-counter <0. There for, your while loop never end because of undefined === undefined.
Returning same sized array to handle nested palis.
ex: abxyxZxyxab => 00030703000 odd numbered nested palis.
ex: asddsa => 003000 even numbered pali.
ex: asdttqwe => 00020000 i dont know if this is a pali but here we go
smallest pali is 2 char wide so i start at index:1 and increment till str.len-1
for (var i = 1; i < str.length-1; i++) {
counter=0;
while(str[i]+1-counter == str[i]+counter || str[i]-counter == str[i]+counter) { // always true when counter is 0
// while (even numbered palis || odd numbered palis)
// IF counter is bigger than 0 but we are still here we have found a pali & middle of the pali is i(or i+0.5) &size of the pali is counter*2(or+1)
if(str[i]+1-counter == str[i]+counter){//even sized pali
res[i]=counter*2;
}else{//odd sized pali
res[i]=counter*2+1;
}
counter++;//see if its a bigger pali.
}
}
not super optimized while + if,else checks same stuff. These can be somehow merged. Maybe even even and odd can be handled without any checks.
You don't need to use three loops. You can do it with two for loops where one starts from the beginning and other one is from the end of the string.
Here we use array reverse() method to match palindromes.
Also I added additional minLength parameter and duplication removal logic to make it more nice.
function findPalindromes(str, minLength) {
var palindromes = [];
var _strLength = str.length;
for (var i = 0; i < _strLength; i++) {
for (var j = _strLength - 1; j >= 0; j--) {
if (str[i] == str[j]) {
var word = str.substring(i, j + 1);
//Check if the word is a palindrome
if (word === word.split("").reverse().join("")) {
//Add minimum length validation and remove duplicates
if(word.length >= minLength && palindromes.indexOf(word) === -1){
palindromes.push(word);
}
}
}
}
}
return palindromes;
}
var result = findPalindromes('asdfmadamasdfbigccbigsdf', 2)
console.log(result)
I am new to JavaScript.
I am trying to write logic such that, if all the brackets are matched in a string of only brackets, there should be bracket matched printed if all brackets match or bracket not matched if the brackets do not match.
var bracketString = "((()()()))";
var match = true;
for (i = 0; i < bracketString.length; i++) {
if (bracketString[i] === "(" && bracketString[i+1] === ")" ) {
i++;
} else if (bracketString[i] != "(" && bracketString[i+1] == ")" ) {
i++;
} else {
i++;
match = false;
}
}
match ? console.log('Brackets matched') : console.log('Brackets not matched');
You need to count and match the starting and ending using a counter. You need to do something like:
Check the total count. The counts of ( should be equal to ).
The total count should be an even number.
We'll have two counters.
The real count is the true sum of the ( as +1 and ) as -1.
The normal count is the current sum (legal) of ( and ) but it doesn't go less than 0.
Only when both the counts are 0, the parentheses are valid.
Use Case and why I use two counts:
))((
For the above case, the count will be: +2, while the realCount will be 0. So this gets invalidated. This way, it not only checks the count of the brackets but also the semantics of it. Hence the two counts.
Code
function validParentheses(parens) {
var count = 0,
realCount = 0;
parens.split("").forEach(function(v) {
if (v == "(") {
count++;
realCount++;
} else if (v == ")") {
count--;
realCount--;
}
if (count < 0)
count = 0;
});
return (count === 0 && realCount === 0);
}
console.log(validParentheses("(())"));
console.log(validParentheses("()()"));
console.log(validParentheses("))(("));
Clever Method
I could also give another clever method:
function validParentheses(parens) {
var indent = 0;
// Go through the parentheses and do for each.
// Keep checking the conditions on the way.
for (var i = 0; i < parens.length && indent >= 0; i++) {
indent += (parens[i] == '(') ? 1 : -1;
}
// If it's 0, then good.
return (indent == 0);
}
console.log(validParentheses("(())"));
console.log(validParentheses("()()"));
console.log(validParentheses("))(("));
Just loop through the string adding to a counter when you see ( and removing from it when you see ). If the result isn't 0, they're not matched:
function check(str) {
var open = 0;
for (var n = 0; n < str.length; ++n) {
var ch = str[n];
if (ch === '(') {
++open;
} else if (ch === ')' && --open < 0) {
return false; // Got ) with no corresponding (
}
}
return open === 0;
}
function test(str) {
console.log(str, check(str));
}
test("((()()()))");
test("((()");
test("()))");
test(")())");
test("()");
test("x");
Approach using String.prototype.match() to create array. Would work if other characters also in string
shift() and splice() remove pairings until none left or a mismatch found
function isMatching(str) {
// create array of only parentheses
var braces = str.match(/\(|\)/g);
// must at least have pairs, but even if they aren't matches will be caught below
var hasPairs = braces.length %2 === 0;
while (braces.length && hasPairs) {
// close can't precede open.
// a close is removed removed from array when open is found
// so at any point in this loop if first element in array is `)` it is a mismatch
if (braces.shift() === ')') break;
// we have an open, look for first close
var closeIdx = braces.indexOf(')');
// if no close for this open, get out of here
if (closeIdx === -1) break;
//or remove matching close
braces.splice(closeIdx, 1);
}
// combination of `shift()` and `splice()` above will remove each pair
// any break above would leave elements in array due to mismatch
return !braces.length;
}
logMatch("(()")
logMatch("((()()()))")
logMatch("(())");
logMatch("()()");
logMatch(")(()");
function logMatch(str){
console.log(str, isMatching(str))
}
I'm late to the party but this is a good exercise
var bracketString = "((()()()))";
var counter=0;
for (i = 0; i < bracketString.length; i++) {
if (bracketString[i] === "(") {
counter++;
} else if (bracketString[i] === ")" ) {
counter --;
}
if (counter < 0){
console.log('Brackets mismatched'); // for example ())(
break;
}
}
if (counter==0){
console.log('Brackets matched');
}
else if (counter>0){
console.log('Brackets mismatched');
}
/* we don't consider counter<0 because before getting out of the for it passes
this validation. */
I'm in the process of learning functional programming, and completely getting rid of for loops has been a challenge sometimes, because they provide so much control and freedom. Below is an example of checking if a string is an isogram or not (no letters should be repeated). With nested for loops, it became an easy solution. Is there a way to do this the functional way with any high order functions or anything else? Any suggestion would be a huge help.
Code:
function isIsogram(string) {
let array = string.split('');
let condition = true;
for (let i = 0; i < string.length; i++) { //first loop picks character
for (j = i + 1; j < string.length; j++) { //second loop compares it rest
if (array[i].toLowerCase() == array[j].toLowerCase())
condition = false; //if repeat, the condition false
}
return condition;
}
}
You can use every or some together with a suitable string function:
function isIsogram(string) {
string = string.toLowerCase(); // case insensitive
return string.split('').every(function(character, index) {
return !string.includes(character, index+1);
});
}
Instead of includes you might also have utilised indexOf.
You can sort the String first and then apply every on it. It will stop the iteration as soon as two successive letters are the same:
Here is an improved implementation. Credit goes to #Xotic750:
function isIsogram(x) {
return Array.from(x.toLowerCase()).sort().every((y, i, xs) => i === 0
? true
: y !== xs[i - 1]);
}
console.log( isIsogram("consumptively") );
console.log( isIsogram("javascript") );
The implementation uses Array.prototype.every's second parameter, which represents the index of the current element (of the iteration). Please note that isIsogram solely depends on functions and their arguments.
Another example, like #Bergi but using some ES6 features for comparison.
function isIsogram(string) {
string = string.toLowerCase(); // case insensitive
for (let character of Array.from(string).entries()) {
if (string.includes(character[1], character[0] + 1)) {
return false;
}
}
return true;
}
console.log(isIsogram('abc'));
console.log(isIsogram('abca'));
How your ES3 style code could have looked (noting some of the issues pointed out in the comments)
function isIsogram(string) {
string = string.toLowerCase(); // case insensitive
var length = string.length;
for (var i = 0; i < length; i += 1) {
for (var j = i + 1; j < length; j += 1) {
if (string.charAt(i) === string.charAt(j)) {
return false;
}
}
}
return true;
}
console.log(isIsogram('abc'));
console.log(isIsogram('abca'));
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 years ago.
Improve this question
Trying to write a simple function to take a string as input, then shift each character over once alphabetically. (a -> b) (f -> g) (z -> a). My function so far is broken. I'm sure there are better ways to go about this, but if someone would be willing to troubleshoot my function that would be awesome. :)
function translate(str) {
var alphabet = ['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'];
str.toLowerCase();
var i = 0;
var j;
//edit: deleted str = ""
while (i < str.length) {
for (j = 0; j < alphabet.length; j++) {
if (str[i] == alphabet[alphabet.length - 1]) { //changed data type
str += alphabet[0]
j=0;
} else if (str[i] == alphabet[j]) {
str += alphabet[j+1]; //fixed this
j=0;
} else {
i++;
}
}
}
return str;
You could also use charCodeAt and fromCharCode to realize your shifting. I might be a little bit more convienent:
function translate(str) {
res = [];
for (var i = 0; i < str.length; i++) {
var ch = str.charCodeAt(i);
//65 => A
//90 => Z
//97 => a
//122 => z
//if ch betweet A and Z or between a and z
if ((ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122)) {
//if z or Z transform to a or A respectively
if (ch === 90 || ch === 122) ch -= 25;
//else increase by one
else ch += 1;
}
res.push(ch);
}
return = String.fromCharCode.apply(this, res);
}
Both methods use unicode representation of the string. Essentially, you transform the single characters into numbers, increase those numbers by one and transform it back to a letter. Here is a unicode table that shows the value of each letter: http://www.utf8-chartable.de/unicode-utf8-table.pl?utf8=dec
Your logic is a little flawed. Just iterate through the string and use the indexOf method along with the modulo operator:
var index = alphabet.indexOf(char.toLowerCase());
if (index === -1) {
// char isn't in the alphabet, so you should skip it
} else {
var newChar = alphabet[(index + 1) % alphabet.length];
}
(index + 1) adds 1 to the index, which selects the next letter, and % alphabet.length makes it wrap around to the beginning in case of z.
Here's one way to do it:
function translate(str) {
var newStr = "";
var alphabet = ['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'];
for (var i = 0; i < str.length; i++) {
var currentLetter = str.substring(i, i+1);
var newLetter = alphabet[(alphabet.indexOf(currentLetter.toLowerCase()) + 1) % alphabet.length];
// preserve the case of the letter
newStr += (currentLetter === currentLetter.toUpperCase()) ? newLetter.toUpperCase() : newLetter;
}
return newStr;
}
The general idea is to loop through each character, find its position in the alphabet array, and add its successor to the new string.
You'll have to add more logic if you need it to handle strings containing symbols, numbers, etc.
I can see a few problems here.
var str = "";. str is the variable you are sending as a parameter, so you reset it with this statement.
if (str[i] == alphabet.length - 1). str[i] and alphabet.length - 1 are not the same data type, so this statement is probably not doing what you think it should. Maybe you should have alphabet[alphabet.length - 1] instead.
else if (str[i] == alphabet[j]) { str += alphabet[j]; //... }. This would add the same letter onto your result string if you didn't reset str like in #1. You should have something like alphabet[(j+1) % alphabet.size] instead.
Also, you should use charAt(i) for getting characters in a string, not subscripts ([]), and you don't have to call j=0 at the end of your for loops, since you already say j=0 in the loop.