Finding matches using regular expression - javascript

How to find the length of consecutive zeros that is surrounded by ones at both ends of a binary?
For example, in 10010001 the 2 matches are 1001 and 10001
1001 the length of zeroes is 2
10001 the length of zeroes is 3
I used match which returned only the last one i.e. 10001.
'1010001'.match(/1(0+)1$/g)

You need lookahead assertions here:
console.log('1010001'.match(/10+(?=1)/g).map(function(x) {
return x.length - 1;
}));

First, replace all '1' by '11', then remove $ - from your regular expression.
console.log('10010001'.replace(/1/g, '11').match(/1(0+)1/g));

In regular expressions, $ is a special character matching the end of the string (MDN).
But that's only half the problem. String#match captured the trailing 1 in the first group and could not create a second overlapping group for '10001'. Try using RegExp#exec instead. The regular expression is stateful, and in this case, you'll want to move the last index back one for each match you find.
var re = /10+1/g;
var str = '10010001';
var matches = [];
var result;
while ((result = re.exec(str)) !== null) {
matches.push(result[0]);
re.lastIndex--;
}
console.log(matches);

Instead of using Regex, I would keep it simple.
Split the string on '1's
Get the length of each section
Remove the first and last section length because they aren't surrounded by '1's
Find the highest section length
const maxNumberOfZeros = Math.max(...'000000101000100000'.split('1').map(str => str.length).slice(1, -1));
console.log(maxNumberOfZeros);

function solution(N) {
let s = (N >>> 0).toString(2).split('');
let max = 0;
//1 1 0 0 1 0 0 0 1 0
if(s.length > 2){
let lastDigit = s[s.length - 1];
let firstDigit = s[0];
while(lastDigit == '0'){
s.pop();
if(!s.length || s.length == 1) return 0;
lastDigit = s[s.length - 1];
}
while(firstDigit == '0'){
s.shift();
if(!s.length || s.length == 1) return 0;
firstDigit = s[0];
}
let x = s.join('').split('1').filter(i => i !== '').sort().reverse();
return x.length ? x[0].length : 0;
}
return 0;
}

The $ within RegExp matches end of string.
You can use Array.prototype.reduce() and logic to evaluate 1 and 0 as a Boolean to determine if the sequence matches the required pattern
let nums = ["1000000001010001", "0000000101", "100000000"];
let countZerosBetweenOnes = str =>
(res => ([...str].reduce((a, b, idx, arr) =>
(!!+a && !+b && arr.find((n, i) => i > idx && !!+n) && res.push(+a)
, !+a && !+b && res.length && ++res[res.length - 1], b))
, res.sort((a, b) => a - b).pop() || 0))([]);
nums.forEach(n => console.log(countZerosBetweenOnes(n)));

Related

How to count all the palindromes in a string using recursion?

I have a recursive function that checks if a string is a palindrome, but my assignment asks me to count the number of palindromes in a string (for example kayak has 2).
I'm really confused about how I can implement a recursive function that counts the number of palindromes. Here's my current code:
function isPalindrome(string) {
if (string.length <= 1) {
return true;
}
let [ firstLetter ] = string;
let lastLetter = string[string.length - 1];
if (firstLetter === lastLetter) {
let stringWithoutFirstAndLastLetters = string.substring(1, string.length - 1);
return isPalindrome(stringWithoutFirstAndLastLetters);
} else {
return false;
}
}
When the function gets a palindrome it is easy:
Record the input
Try again without the edges
Stop when input is three characters or less
"kayak" -> "aya"
If the input isn't a palindrome try "both ends" recursively e.g. with "kayam" try with both "kaya" and "ayam" and keep going...
We stop the recursion when a string is 3 (or less) characters. A single character is not a palindrome and checking whether a two or three characters string is a palindrome is trivial.
kayam
|
+-------------+
| |
kaya ayam
| |
+-------+ +--------+
| | | |
kay aya aya yam
const reverse =
([...xs]) =>
xs.reverse().join("");
const is_palindrome =
a =>
a.length === 1 ? false
: a.length <= 3 ? a[0] === a[a.length-1]
: a === reverse(a);
const find_palindromes = str => {
const scan =
(x, xs = []) =>
x.length <= 3 ? xs.concat(is_palindrome(x) ? x : [])
: is_palindrome(x) ? xs.concat
( x
, scan(x.slice(1, -1))
)
: xs.concat
( scan(x.slice(0, -1))
, scan(x.slice(1))
);
return [...new Set(scan(str))];
};
console.log(find_palindromes("kayak").join());
console.log(find_palindromes("kayakkayak").join());
console.log(find_palindromes("kayakcanoe").join());
console.log(find_palindromes("kayam").join());
console.log(find_palindromes("appal").join());
console.log(find_palindromes("madamimadam").join());
console.log(find_palindromes("madamimadamkayak").join());
I think the accepted answer does not actually work. It will not count palindromes unless they are centered in the string and will count substrings that are not palindromes if as long as they start and end with the same letter. The answer from CertainPerformance would probably work but I think it would result in checking a lot of strings that don't need to be checked. Here's what I came up with, I think it works for the extra tests I've added.
function countPalindromes(string) {
if (string.length <= 1) {
return 0;
}
count = 0
for ( var i = 0; i < string.length; i++ ) {
count += countPalindromesCenteredAt(string, i)
count += countPalindromesCenteredAfter(string, i)
}
return count
}
function countPalindromesCenteredAt(string, i) {
count = 0
for ( var j = 1; i-j>=0 && i+j < string.length; j++ ) {
if (string.charAt(i-j) === string.charAt(i+j)) {
count += 1
}
else {
return count
}
}
return count
}
function countPalindromesCenteredAfter(string, i) {
count = 0
for ( var j = 1; i-j>=0 && i+j < string.length; j++ ) {
if (string.charAt(i-j+1) === string.charAt(i+j)) {
count += 1
}
else {
return count
}
}
return count
}
console.log(countPalindromes("kayak"));
console.log(countPalindromes("aya"));
console.log(countPalindromes("kayakcanoe"));
console.log(countPalindromes("kcanoek"));
One method would be to first get all substrings, then validate each:
getAllSubstrings('kayak').filter(str => str.length >= 2 && isPalindrome(str))
function getAllSubstrings(str) {
var i, j, result = [];
for (i = 0; i < str.length; i++) {
for (j = i + 1; j < str.length + 1; j++) {
result.push(str.slice(i, j));
}
}
return result;
}
function isPalindrome(string) {
if (string.length <= 1) {
return true;
}
let [ firstLetter ] = string;
let lastLetter = string[string.length - 1];
if (firstLetter === lastLetter) {
let stringWithoutFirstAndLastLetters = string.substring(1, string.length - 1);
return isPalindrome(stringWithoutFirstAndLastLetters);
} else {
return false;
}
}
console.log(
getAllSubstrings('kayak').filter(str => str.length >= 2 && isPalindrome(str))
);
Here's an answer similar to that from CertainPerformance, but using recursion for the helper functions:
const getSubstrings = (str) =>
str .length == 0
? []
: [
... str .split ('') .map ((_, i) => str .slice (0, str .length - i)),
... getSubstrings (str .slice (1))
]
const isPalindrome = (str) =>
str .length < 2
? true
: str [0] === str .slice (-1) [0] && isPalindrome (str .slice (1, -1))
const getPalindromicSubstrings = (str) =>
getSubstrings (str)
.filter (s => s.length > 1)
.filter (isPalindrome)
const countPalindromicSubstrings = (str) =>
getPalindromicSubstrings (str) .length
const countUniquePalindromicSubstrings = (str) =>
new Set(getPalindromicSubstrings (str)) .size
console .log (getPalindromicSubstrings ('madamimadam'))
console .log (countPalindromicSubstrings ('madamimadam'))
console .log (countUniquePalindromicSubstrings ('madamimadam'))
.as-console-wrapper {max-height: 100% !important; top: 0}
getSubstrings does just what you'd expect. getSubstrings('abcd') returns ["abcd", "abc", "ab", "a", "bcd", "bc", "b", "cd", "c", "d"].
isPalindrome says that the empty string and single-character strings are automatically palindromes and that for another string we check that the two end characters match, recurring on the remainder.
getPalindromicSubstrings finds all the substrings that are palindromes, skipping those of length 1.
countPalindromicSubstrings returns a count of those.
countUniquePalindromicSubstrings uses a Set to filter out duplicates and returns that count.
We could also easily write a getUniquePalindromicSubstrings in a similar manner if needed.
getSubstrings is the only function with any complexity. It operates by repeatedly slicing our string from to a value varying from length down to 1, then recurring on the string starting with the second character, stopping when our input is empty.

Get substings between Specific Characters

I am trying to solve some JS problem. I want to check if an IP address is a valid one.
So the numbers must be between 0-255.
So what I want to do at this point, is to get an IP ex 192.168.1.1 and get substrings and load them to an array, so I want to create an array that looks like that:
array = ['192' , '168' , '1' , '1'];
I've tried various approaches in my algorithm but can't manage to target dynamically the numbers and split them between every dot.
I've done several tries, and thats the closest I could get.
let str = '192.168.1.1';
isValidIp(str);
function isValidIP(str) {
let array = [];
let substringArray = [];
for (let i=0; i<str.length; i++){
if (str[i] == '.') array.push(i);
}
let counter = 0;
for (let i in array){
substringArray.push(str.substring(counter, array[i]));
counter = array[i];
}
console.log(substringArray);
}
Which returns:
[ '192', '.168', '.1' ]
You can use the split() function of JavaScript which returns an array of every element separated by the digit specified. Or, which I wouldn't recommend, you could use RegEx. Here is an example of both:
function isValidIPwRegEx(str){
if (/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(str))
{
return true;
}
return false;
}
function isValidIP(str) {
let array = str.split("."),
isIP = true;
array = array.filter( block => !block.includes("+") && !block.includes("e") );
if(array.length!=4) return false;
array.forEach((number) => {
if ( !(+number >=0 && +number <= 255) ) { //As #p.s.w.g kindly suggested
isIP = false;
}
});
return isIP;
}
//With RegEx
console.log("With RegEx");
console.log(isValidIPwRegEx("192.168.1.1"));
console.log(isValidIPwRegEx("blah.blah.blah.blah")); //As #georg suggested
console.log(isValidIPwRegEx("1e1.2e1.+3e1.+5e1")); //As #georg again suggested to #Nina Scholz
console.log("");
//Without RegEx
console.log("Without RegEx");
console.log(isValidIP("192.168.1.1"));
console.log(isValidIP("blah.blah.blah.blah")); //As #georg suggested
console.log(isValidIP("1e1.2e1.+3e1.+5e1")); //As #georg again suggested to #Nina Scholz
console.log(isValidIP("1e1.2e1.3e1.5e1"));
Use String's split function.
So, something like "192.168.1.1".split(".")
You could split the string and check if the length is four and all values are integers and smaller than 256.
var ip = '192.168.1.1',
values = ip.split('.'),
valid = values.length === 4 && values.every(v => +v >= 0 && +v < 256);
console.log(values);
console.log(valid);
function isValidIP(str) {
let re = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
let m = str.match(re);
return m &&
m[1] >= 0 && m[1] <= 255 &&
m[2] >= 0 && m[2] <= 255 &&
m[3] >= 0 && m[3] <= 255 &&
m[4] >= 0 && m[4] <= 255
;
}
If you wish to be more precise, each digit check can be:
(0|[1-9]\d{0:2})
This prevents extraneous leading 0's.

encrypt words cycle shift value to each letter from the end of its word java script

I want to encrypt words cycle shift value to each letter from the end of its word java script.
The value of this cycle shift is determined by the postion of the letter from the end of it's word. The shift value for each letter of word is its index value (starting from 0) right most character of the word.
function encryptWords(str) {
// you can comment this line
str = str.toLowerCase();
var result = '';
var charcode = 0;
for (var i = str.length -1 ; i > = 0; i--) {
charcode = (str[i].charCodeAt()) + i;
result += String.fromCharCode(charcode);
}
return result;
}
console.log(encryptWords('yum feed'));
for example, the shift values in "yum feed" are
yum : m-> 0, u -> 1, y -> 2;
feed : d->0, e->1, e->2, f->3
"avm igfd"
Your code works fine for a single word.
But for a string containing 2 or more words, you can try using Array.map() twice: once for words and once for chars in those words.
function CaesarCipher(str, num) {
// you can comment this line
str = str.toLowerCase();
var charcode = 0;
var result = str.split(' ').map(
val => val.split('').map(
(c, i) => {
// shifting of the chars, based on their index values
charcode = 97 + (
(c.charCodeAt() + val.length - 1 - i) - 97
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ shift
) % 26;
// ^^^^ to avoid chars higher than 'z'
return String.fromCharCode(charcode);
}
).join('')
).join(' ');
return result;
}
console.log(CaesarCipher('yum feed', 2));
function encryptWords(str) {
return str
.split(' ')
.map(s => [...s]
.map((c, i) => String.fromCharCode(97 + (c.charCodeAt(0) + s.length - i - 98) % 26))
.join('')
)
.join(' ');
}
console.log(encryptWords('yum feed'));

javascript check if string has one of set of chars

I am implementing password complexity checks, one of the requirement is that the password contain at least one of a set of chars.
How would I code an efficient function for that, ie
function hasOneOfChars(s, chars) {
// assuming both s and chars are strings,
// return true iff s has at least one char from chars
}
Thanks!
Instead of using above function Try using it with regex pattern.
Contain at least 8 characters
contain at least 1 number
contain at least 1 lowercase character (a-z)
contain at least 1 uppercase character (A-Z)
contains only 0-9a-zA-Z
if you want set of chars use below pattern, Moreover {4,} is atleast contain charaters so you can change according to your requirement
[a-zA-Z0-9]{4,}
Thanks.
By requirement to receive 2 strings arguments. You can try the following code.
function hasOneOfChars(s, chars) {
for (let char of chars.split(""))
if (s.indexOf(char) > -1) {
return true;
}
return false;
}
console.log(hasOneOfChars("abcd", "abcasjkdf"));
You could sort both strings and do the following :
var s = "ytreza".split("").sort().join("");
var chars = "ytrewq".split("").sort().join("");
hasOneOfChars(s, chars); // true
function hasOneOfChars (s, chars) {
var a, b, i, n;
if (s < chars) a = s, b = chars;
else a = chars, b = s;
i = 0, n = a.length;
while (i < n && a[i] !== b[0]) i++;
return i < n;
}
Using sorted strings you don't have to loop through both strings anymore :
a = "azerty"
b = "qwerty"
n = a.length // 6
i = 0, a[i] === b[0] ? false
i = 1, a[i] === b[0] ? false
i = 2, a[i] === b[0] ? true
return i < n // true
(credit goes to CertainPerformance)
// assuming both s and chars are strings, return true iff s has at least one char from chars
function hasOneOfChars(s, chars) {
return [...chars].some(char => s.includes(char));
}
console.log(hasOneOfChars('aaabbb', 'ax'));
console.log(hasOneOfChars('aaabbb', 'xy'));

How to split a string based on commas except inside parenthesis

I have a string that contains a create query, and I'm trying to split it based on commas. Unfortunately, some of the lines have commas in them surrounded by parenthesis.
Example:
dbo.Display_Test1.Column,
CASE WHEN CHARINDEX(' To ', Test3) > 0 AND CHARINDEX('XVR', Test3) = 0 THEN LEFT(Test3, (CHARINDEX(' To ', Test3) - 12)) ELSE Test3 END AS Test3,
dbo.Display_Test2.Column,
ISNULL((CASE WHEN [2-Display-Test4].[Total Number] > 0 AND [1-Display-Test5].SumOfNumber = 0 THEN 0 ELSE (([2-Display-Test4].[Total Number] * 1000) / [1-Display-Test5].SumOfNumber) END), 0) AS Test6,
I want to split my string based on commas that aren't inside of (possibly multiple) parenthesis. For reference, my example should be split where the line breaks are (although my string doesn't have the line breaks in it).
I've tried a number of different solutions, but none work quite right:
/(?:\(*[^()]*\)|[^,])+/g works on lines 1,3, and 4, but fails on line 2. It breaks up the line into multiple matches.
/((?:[^,(]+|(\((?:[^()]+)|$1\)))+)/g works on lines 1,2, and 3, but fails on line 4. It also breaks up the line into multiple matches.
I can't quite seem to make it work. Any help is appreciated.
One solution possible : ( note : we remove the separations comas )
var str = "dbo.Display_Test1.Column, CASE WHEN CHARINDEX(' To ', Test3) > 0 AND CHARINDEX('XVR', Test3) = 0 THEN LEFT(Test3, (CHARINDEX(' To ', Test3) - 12)) ELSE Test3 END AS Test3, dbo.Display_Test2.Column, ISNULL((CASE WHEN [2-Display-Test4].[Total Number] > 0 AND [1-Display-Test5].SumOfNumber = 0 THEN 0 ELSE (([2-Display-Test4].[Total Number] * 1000) / [1-Display-Test5].SumOfNumber) END), 0) AS Test6,";
// if the string don't end by a coma , we add it.
var strArr = str.replace(/,*\s*$/ , ',').split('');
var res;
res = strArr.reduce(function( trans , charValue ){
if(charValue === '(') {
trans.deep++;
}
if(charValue === ')') {
trans.deep--;
}
if( trans.deep === 0){
if(charValue===',') {
trans.arr.push( trans.str);
trans.str = '';
}else{
trans.str += charValue;
}
}else{
trans.str += charValue;
}
return trans;
}, { arr : [] , str : '' ,deep : 0});
document.write('<pre>' + JSON.stringify(res.arr , null , ' ') + '</pre>');
Edit :
As suggested by Thriggle in the comments i've added the case where the string don't end by a coma.
changed variables name .
Of course if we want to work with unlimited nesting of parentheses then it would be impossible to avoid a loop. Still for some bounded nesting we do can write a regular expression capturing it.
For up to 1 parentheses level it could be
/((?:[^(),]|\([^()]*\))+)/g
For up to 2 parentheses level (probably it's your case)
/((?:[^(),]|\((?:[^()]|\([^()]*\))*\))+)/g
And so on, recursively substituting [^()]* with (?:[^()]|\([^()]*\))*
Regex won't get you where you want and still allow arbitrary complexity.
One approach is to iterate through each character in the string, keeping track of when you hit open and close parentheses, and only splitting on commas when the numbers of open and close parentheses cancel each other out (so you know you're not within a parenthetical statement).
function splitQuery(input){
var arr = [];
var lastStart = 0;
var open = 0;
for(var i = 0, len = input.length; i < len; i++){
var curr = input[i];
if(curr === "("){open += 1;}
else if(curr === ")"){ open = open < 1 ? 0 :open -=1;}
else if(curr === ","){
if(open === 0){
arr.push(input.substring(lastStart,i));
lastStart = i+1;
}
}else if(i+1 === len){
arr.push(input.substring(lastStart,i+1));
}
}
return arr;
}
Just be aware that this approach can be expensive (from a performance perspective) when dealing with especially large strings.
Here's a working example:
var query = "dbo.Display_Test1.Column, CASE WHEN CHARINDEX(' To ', Test3) > 0 AND CHARINDEX('XVR', Test3) = 0 THEN LEFT(Test3, (CHARINDEX(' To ', Test3) - 12)) ELSE Test3 END AS Test3, dbo.Display_Test2.Column, ISNULL((CASE WHEN [2-Display-Test4].[Total Number] > 0 AND [1-Display-Test5].SumOfNumber = 0 THEN 0 ELSE (([2-Display-Test4].[Total Number] * 1000) / [1-Display-Test5].SumOfNumber) END), 0) AS Test6,";
var split = splitQuery(query);
var output = document.getElementById("output")
for(var el in split){
output.insertAdjacentHTML("beforeend",el + ": "+ split[el]+"<hr/>");
}
function splitQuery(input){
var arr = [];
var lastStart = 0;
var open = 0;
for(var i = 0, len = input.length; i < len; i++){
var curr = input[i];
if(curr === "("){open += 1;}
else if(curr === ")"){ open = open < 1 ? 0 :open -=1;}
else if(curr === ","){
if(open === 0){
arr.push(input.substring(lastStart,i));
lastStart = i+1;
}
}else if(i+1 === len){
arr.push(input.substring(lastStart,i+1));
}
}
return arr;
}
<div id="output"></div>

Categories