How to find which group was captured in Javascript? - javascript

I have a regular expression /(q)|([zZ])|(E)/.
My question is, how to get WHICH group was matched.
So, if I do
"ZqE".replace(/(q)|([zZ])|(E)/g, /* ??? */)
How do I get the output "213"?

You can do something like this, the group which matches will hold the value remaining one will be undefined
let mapper = {
'g1': 1,
'g2': 2,
'g3': 3
}
let final = "ZqE".replace(/(q)|([zZ])|(E)/g, (m, g1, g2, g3) => {
return g1 !== undefined && mapper['g1'] || g2 !== undefined && mapper['g2'] || g3 !== undefined && mapper['g3']
})
console.log(final)

You can use the result array from exec to figure out which group was matched:
let re = /(q)|([zZ])|(E)/g;
while (result = re.exec('ZqE')) {
console.log(result.findIndex((v, i) => i && typeof(v) !== 'undefined'));
}

In the pattern you specify 3 capturing groups which are numbered from 1 to 3.
If you want to get "213" and you know that you want to convert q to 1, zZ to 2 and E to 3, as an alternative you could do that by checking the values of the match using replace.
let result = "ZqE".replace(/[ZzEq]/g, function(m) {
if (m.toLowerCase() === 'z') return 2;
if (m === 'q') return 1;
if (m === 'E') return 3;
});
console.log(result);

Related

How to add a comma separator after each params if param has values and dot in the end of params

for example we have 3 params
function test(a,b,c){
console.log(a + ',' + b + ',' + c +'.')
}
test('a','b','c')
will print
> a, b, c.
but in case b will be empty, the result will look with two comma, like
test('a','','c')
will print
a,,c.
we can improve like checking each var like this
function test(a,b,c){
console.log(a + (a?',':'') + b + (b?',':'') + c +(c?'.':''))
}
so now
test('a','','c')
will print
a,c.
look ok, but when we have only b
test('','b','')
will print
b,
and now we must to check values a or b exist and there is no c to print '.'
and complexity increases, but what if we have n vars, any ideas how to solve more easily
expected result is :
test('a','b','c') => a, b, c.
test('a','b','') => a, b.
test('a','','') => a.
test('','b','c') => b, c.
test('','b','') => b.
test('','','') =>
You can filter and use the ...rest parameter
const isParm = prm => prm !== "" && prm !== undefined && prm !== null;
const test = (...theArgs) => {
const list = theArgs.filter(parm => isParm(parm));
if (list.length > 0) console.log(`${list.join(",")}.`)
}
let x;
test('a','b','c')
test('a',null,'c'); // null is a falsy value
test('','','c')
test('a')
test('','b')
test(x); // undefined (falsy)
test(0,0,0); // falsy values
If I understand your intention, this should do it.
function test(...args) {
const res = args.filter(i => i).join(',')
return res + (res && ".")
}
// Usage
console.log(test('a','','c')); // "a,c."
console.log(test('a','b','c')); // "a,b,c."
console.log(test('','','c')); // "c."
console.log(test('a', '', '')) // "a."
console.log(test('', '', '')) // ""
Use Array.reduce() function to iterate each argument in argument list and add comma separator then return final string.
function test(...args){
return args.reduce((prev, current, index) => {
if (current.length !== 0) {
const separator = prev.length === 0 ? "" : ",";
prev += `${separator}${current}`
}
if (index === args.length - 1) {
prev += ".";
}
return prev;
}, "");
}
console.log(test('a','','c'));
Using an array join approach, we can add all inputs to an array. Then filter off empty entries and join by a comma separator.
function test(a,b,c) {
var array = [a, b, c];
return array.filter(x => x).join(", ") + ".";
}
console.log(test('a','','c'));
Actually, a better function signature for your function would accept an array, which allows for concat with separator of any number of items.

find multiple (2) elements in a string in javascript

I want to return whether one of the conditions exist within findAB(str) as boolean
A 5 letter long string which starts with 'a' ends with 'b'
A 5 letter long string which starts with 'b' ends with 'a'
function findAB(str) {
let lowerSTR = str.toLowerCase()
let indexOFa = lowerSTR.indexOf('a')
let indexOFb = lowerSTR.indexOf('b')
if (lowerSTR[indexOFa + 4] === 'b' || lowerSTR[indexOFb + 4] === 'a') {
return true;
}
return false;
}
I first changed strings into lower case using .toLowerCase() method and defined indexOFa and indexOFb.
I thought simply by doing index of a + 4 will turn out true but in fact it doesn't and cannot figure out what I did wrong.
I also know some method to find elements in an array such as find(), includes(), map() or filter but not sure if I can use it since it is not an array.
If you want to change your approach you can use regex like this
function findAB(str) {
let case1 = str.match(/a[a-z]*b/g); // get all substrings starting with a and ending with b
let case2 = str.match(/b[a-z]*a/g); // get all substrings starting with b and ending with a
case1.forEach((matchedString) => {
if (matchedString.length == 5) {
return true;
}
});
case2.forEach((matchedString) => {
if (matchedString.length == 5) {
return true;
}
});
return false; // return false if no substrings matching condition exists
}
If you need to tell the difference between which case was triggered this could be a cleaner solution to read.
If you want to search for a pattern of 5 characters which starts and ends with (a and b) or (b and a) irrespective of case and any length of string, you can do something like this.
The regex considers that the string "a _ _ _ b" or "b _ _ _ a" can have only alphabets in any case not any other character, if you are accepting any other character you can update [a-zA-Z] part in the regex.
function findAB(str) {
const regexp = /(.?(((a|A)[a-zA-Z]{3}(b|B))|((b|B)[a-zA-Z]{3}(a|A))).?)/;
return regexp.test(str);
}
If it is fixed that length of string should be 5 and you are just dealing with first and last character you can simply use the following function
function findAB(str) {
const lengthOfString = str.length;
if (lengthOfString === 5) {
const firstCharacter = str[0].toLowerCase()
const lastCharacter = str[lengthOfString-1].toLowerCase()
return (firstCharacter === 'a' && lastCharacter === 'b') || (firstCharacter === 'b' && lastCharacter === 'a');
}
// returns false string does not contains 5 characters
return false;
}
Correcting your solution.
The problem with your solution is that indexOf returns -1 if the no match is found. So findAB("bbbbb") will return true, because indexOFa variable will be equal to -1 and lowerSTR[indexOFa + 4] will check the second last character and the condition will evaluate to true. So the following code will solve your problem.
function findAB(str) {
let lowerSTR = str.toLowerCase()
let indexOFa = lowerSTR.indexOf('a')
let indexOFb = lowerSTR.indexOf('b')
if ((indexOFa >= 0 && lowerSTR[indexOFa + 4] === 'b') || (indexOFb >= 0 && lowerSTR[indexOFb + 4] === 'a')) {
return true;
}
return false;
}
Also, I wouldn't prefer hardcoding 4 better use str.length-1.
You could also just simply do it like this
function findAB(str) {
let lowerSTR = str.toLowerCase();
let indexOFa = lowerSTR.indexOf('a');
let indexOFb = lowerSTR.indexOf('b');
if (
(indexOFa === 0 && indexOFb === str.length - 1) ||
(indexOFb === 0 && indexOFa === str.length - 1)
)
return true;
return false;
}

Return the first non-repeating character of a string

In the first chunk of my code I have an ' if ' statement that is not working as it should, and I can't figure out why.
When using the argument 'hous', it should enter the first ' if ' statement and return 0. It returns -1 instead.
var firstUniqChar = function(s) {
for (let i = 0; i < s.length; i++){
let letter = s[i];
// console.log('s[i]: ' + letter);
// console.log(s.slice(1));
// console.log( 'i: ' + i);
if ((i = 0) && !(s.slice(1).includes(letter))) {
return 0;
}
if ((i = s.length - 1) && !(s.slice(0, i).includes(letter))) {
return 1;
}
if(!(s.slice(0, i).includes(letter)) && !(s.slice(i + 1).includes(letter))) {
return 2;
}
}
return -1;
};
console.log(firstUniqChar("hous"));
This is another way you can write your function:
const firstUniqChar = s => [...s].filter(c=>!(s.split(c).length-2))[0] || -1;
console.log(firstUniqChar("hous"));
console.log(firstUniqChar("hhoous"));
console.log(firstUniqChar("hhoouuss"));
Look up method for scattered repeated characters and functional find()-based approach
You may break your input string into array of characters (e.g. using spread syntax ...) and make use of Array.prototype.find() (to get character itserlf) or Array.prototype.findIndex() (to get non repeating character position) by finding the character that is different from its neighbors:
const src = 'hhoous',
getFirstNonRepeating = str =>
[...str].find((c,i,s) =>
(!i && c != s[i+1]) ||
(c != s[i-1] && (!s[i+1] || c != s[i+1])))
console.log(getFirstNonRepeating(src))
.as-console-wrapper{min-height:100%;}
Above will work perfectly when your repeating characters are groupped together, if not, I may recommend to do 2-passes over the array of characters - one, to count ocurrence of each character, and one more, to find out the first unique:
const src = 'hohuso',
getFirstUnique = str => {
const hashMap = [...str].reduce((r,c,i) =>
(r[c]=r[c]||{position:i, count:0}, r[c].count++, r), {})
return Object
.entries(hashMap)
.reduce((r,[char,{position,count}]) =>
((count == 1 && (!r.char || position < r.position)) &&
(r = {char, position}),
r), {})
}
console.log(getFirstUnique(src))
.as-console-wrapper{min-height:100%;}
function nonRepeat(str) {
return Array
.from(str)
.find((char) => str.match(newRegExp(char,'g')).length === 1);
}
console.log(nonRepeat('abacddbec')); // e
console.log(nonRepeat('1691992933')); // 6
console.log(nonRepeat('thhinkninw')); // t

Javascript | Dynamic array of Characters based on consecutive letters conditions

I was doing a codewar challange, and couldn't find a solution, but I really want to know how we can solve this problem.
So we getting two integers, let's say N and D and we should return a string containing exactly N letters 'n' and exactlly D letters d with no three consecutive letters being same.
For example if we get N=5 and D=3 we should return "nndnndnd" or "nbnnbbnn" or any other correct answer
another example like if we get N=1 D=4 the only accepted answer should be "ddndd"
What I did was making a helper function like this :
function generateArray (char,q){
let arr= []
for(let i=0; i<q; i++){
arr.push(char)
}
return arr
}
and inside the main function :
function solution(N, D) {
let arrayOfchar = generateArray('n',N)
arrayOfchar.reduce((prev,current,index) => {
for(let i=0; i<D; i++) {
if(prev===current) {
arrayOfchar.splice(index, 0, "d")
}
}
})
}
But I don't know hoe should I put the "d" only after two or less consecutive "n"
Anyone clue?
Rather than creating an entire array of the same character at the very start, I think it would make more sense to create the array piece-by-piece, until N and D come out to 0.
Here's one possible implementation. The general idea is to try to push whichever character count is larger, or if that's not possible due to 3-in-a-row, push the other character, and subtract the appropriate character count by one. Repeat until both counts are 0:
function solution(n, d) {
const arr = [];
function canPush(char) {
const { length } = arr;
return (arr[length - 1] !== char || arr[length - 2] !== char);
}
function push(char) {
arr.push(char);
if (char === 'n') n--;
else if (char === 'd') d--;
}
while (n > 0 || d > 0) {
if (n > d) {
if (canPush('n')) push('n');
else if (d === 0) return console.log('Impossible');
else push('d');
} else if (d >= n) {
if (canPush('d')) push('d');
else if (n === 0) return console.log('Impossible');
else push('n');
}
}
console.log(JSON.stringify(arr));
// return arr;
}
solution(5, 3);
solution(1, 4);
solution(1, 5);
solution(5, 1);
solution(2, 5);
solution(2, 6);
solution(2, 7);
Here is another solution to this interesting problem. The idea is not to go one by one but to figure which one is the larger number and then do an array of pairs of that letter while doing a simple array of the smaller and then just concat them one with another ... so you have 5 and 3 ... nn + d + nn + d + n. 2 pairs of the bigger plus one of the smaller etc.
const fillArray = (length, letter, bigNumber) => {
var arr = []
for(var index=0; index < length; index++) {
arr.push([letter, bigNumber%2 && index+1 === length ? null : letter])
}
return arr;
}
const getString = (n, d) => {
var swtch = d > n, arr = [],
bigger = {number: swtch ? d : n, letter: swtch ? 'd' : 'n', ceil: Math.ceil((swtch ? d : n)/2)},
smaller = {number: swtch ? n : d, letter: swtch ? 'n' : 'd', ceil: Math.ceil((swtch ? n : d)/2)}
if(Math.abs((bigger.number/2) - smaller.number >= 1.5)) {
return 'Not possible with given parameters!'
}
var bigWorkArray = fillArray(bigger.ceil, bigger.letter, bigger.number)
var smallWorkArray = n === d ? fillArray(smaller.ceil, smaller.letter, smaller.number) : Array(smaller.number).fill(smaller.letter)
for(var i=0; i < bigWorkArray.length; i++) {
arr.push(...bigWorkArray[i],...smallWorkArray[i] || '')
}
return arr.join('');
}
console.log(getString(5,3))
console.log(getString(1,4))
console.log(getString(1,5))
console.log(getString(5,1))
console.log(getString(2,5))
console.log(getString(2,6))
console.log(getString(2,7))

Recursive palindrome check with JavaScript

I am trying to find out whether a string is a palindrome by recursion using javascript. But I can't figure out what I am missing in the code.
var firstCharacter = function(str) {
return str.slice(0, 1);
};
var lastCharacter = function(str) {
return str.slice(-1);
};
var middleCharacters = function(str) {
return str.slice(1, -1);
};
var isPalindrome = function(str) {
if(str.length < 2) {
return true;
} else {
if(firstCharacter(str) == lastCharacter(str)) {
isPalindrome(middleCharacters(str));
} else return false;
}
};
var checkPalindrome = function(str) {
console.log("Is this word a palindrome? " + str);
console.log(isPalindrome(str));
};
checkPalindrome("a");
//Program.assertEqual(isPalindrome("a"), true);
checkPalindrome("matom");
//Program.assertEqual(isPalindrome("motor"), false);
checkPalindrome("rotor");
//Program.assertEqual(isPalindrome("rotor"), true);
For sure something is wrong with the recursive call. I would love to have your help. Thanks. I am attaching the output of my code.
Here is another recursive palindrome.
function checkPalindrome(str){
if(str.length === 1) return true;
if(str.length === 2) return str[0] === str[1];
if(str[0] === str.slice(-1)) return checkPalindrome(str.slice(1,-1))
return false;
}
console.log(checkPalindrome('a')) // true
console.log(checkPalindrome('matom')) // false
console.log(checkPalindrome('rotor')) // true
You defined isPalindrome() to return a value, so if you call it yourself, recursively or otherwise, you need to deal with that return value. Also, your if ... else logic is too complicated, simplify:
var isPalindrome = function(str) {
if (str.length < 2) {
return true;
}
if (firstCharacter(str) == lastCharacter(str)) {
return isPalindrome(middleCharacters(str));
}
return false;
};
const isPalindrome = str => {
const strLen = str.length;
if (strLen < 2) return true;
if (str[0] === str[strLen - 1]) {
return isPalindrome( str.slice(1, strLen - 1) );
}
return false;
};
console.log(isPalindrome('madam'));
Using slice creates an array - if you want to compare the first and last char, you will need to extract the value from the array before applying == -
var firstCharacter = function(str) {
return str.slice(0, 1)[0] // <-- get the first element of the slice
}
var lastCharacter = function(str) {
return str.slice(-1)[0] // <-- get the first element of the slice
}
Here's another recursive solution that uses parameters l (left) and r (right) to check the string using indexes (rather than creating intermediate values with slice) -
const palindrome = (s = "", l = 0, r = s.length - 1) =>
r - l < 2
? true
: s[l] === s[r] && palindrome (s, l + 1, r - 1)
console.log
( palindrome ("motor") // false
, palindrome ("rotor") // true
, palindrome ("racecar") // true
, palindrome ("wow") // true
, palindrome ("i") // true
)
And here's a mutually recursive definition. It's wasteful but it has an elegant form nonetheless -
const pal = ([ s, ...more ]) =>
more.length === 0 || pal2 (more.reverse(), s)
const pal2 = ([ s, ...more ], q) =>
s === q && pal (more.reverse())
console.log
( pal ("motor") // false
, pal ("rotor") // true
, pal ("racecar") // true
, pal ("wow") // true
, pal ("i") // true
)
Here is another way to recursively check for a palindrome in JS:
function isPalindrome(str){
if (str[0] === str[str.length - 1] && str.length > 1) {
isPalindrome(str.substring(1, str.length -1))
return true
}else{
return false
}
}
Here's a simple answer for ya. Basically we are comparing the first character to last character and acting accordingly.
const isPalindrome = str => {
if (str.length <= 1) return true;
if (str[0] !== str[str.length - 1]) return false;
return isPalindrome(str.slice(1,-1))
}
const isPalindrome = str => {
// base case
if(str.length === 1) return true;
if(str.length === 2) return str[0] === str[1];
if(str[0] === str[str.length - 1]) {
return isPalindrome(str.slice(1, -1))
}
return false;
}
you can use recursion
base case
we have a base case (the simple case) if the string is one char we simply returns true.
if it has two chars we check if the first char is identical to the second and we return true if they are.
recursive case
if it is more than two chars we check if the first and last chars are identical or not if they are not we simply return false
but if they are identical so we now want to do the same thing with other chars so we call the same function with the same string but removing the first and last chars because we already know that they are identical and we keep going until we reach the base case.
hope this be useful
some tests
isPalindrome('p') // true
isPalindrome('po') // false
isPalindrome('pp') // true
isPalindrome('pop') //true
What's about this solution ?
function isPalindrome(str){
if (str.length > 3) return isPalindrome(str.substring(1, str.length-1));
return str[0] === str[str.length-1];
}
My simple implementation for a recursive palindrome check, in 2022:
function isPalindrome(str) {
if (!str.length || str.length === 1) return true;
return str[0] === str.at(-1) ? isPalindrome(str.substr(1, str.length - 2)) : false;
}
console.log(isPalindrome('catotac'));
Iterations breakdown:
// 1st iteration:
isPalindrome('catotac');
//2nd iteration
isPalindrome('atota');
//3rd
isPalindrome('tot');
// 4th iteration
isPalindrome('o'); // true

Categories

Resources