Write a function that reverses characters in (possibly nested) parentheses in the input string.
Input strings will always be well-formed with matching ()s.
For inputString = "(bar)", the output should be
reverseInParentheses(inputString) = "rab";
For inputString = "foo(bar)baz", the output should be
reverseInParentheses(inputString) = "foorabbaz";
For inputString = "foo(bar(baz))blim", the output should be
reverseInParentheses(inputString) = "foobazrabblim".
[input] string inputString
A string consisting of lowercase English letters and the characters ( and ). It is guaranteed that all parentheses in inputString form a regular bracket sequence.
Guaranteed constraints:
0 ≤ inputString.length ≤ 50.
[output] string
Return inputString, with all the characters that were in parentheses reversed.
My Solution
Java Script
function reverseInParentheses(inputString) {
let arr = inputString
let start = arr.indexOf(')') < arr.lastIndexOf('(') ? arr.indexOf('(') : arr.lastIndexOf('(')
let end = arr.indexOf(')')
let temp = arr.substring(start + 1, end)
if(start !== -1 && end !== -1){
return reverseInParentheses(arr.substring(0, start) +
[...temp].reverse().join('') +
arr.substring(end + 1))
}
return arr
}
Problem
I am passing all cases except for final hidden case, no runtime or execution time limit error is being returned. So I am having trouble figuring out what scenario is causing the fail. I really want to use my own solution instead of copying the regex ones and in my mind this solution should work, perhaps a more experienced mind can show my folly. Thanks in advance.
The problem is that your calculation of start and end really don't work. And there's no simple fix to this problem.
The comment from Jonas Wilms suggests trying '((see)(you))'. For this test case, you will get start and end like this:
0 5
((see)(you))
^ ^
start ----' '---- end
Note that the start and end are not an actual pair here. There's another '(' in between.
You can fix this up by doing a more sophisticated calculation of these values, by iterating through the characters, updating start every time you hit a '(' and updating end when you hit a ')', then stopping.
That might look like this:
function reverseInParentheses(inputString) {
let arr = inputString
let i = 0, start = 0, end = -1
while (end < start && i < arr.length) {
if (arr[i] == '(') {start = i}
if (arr[i] == ')') {end = i}
i++
}
let temp = arr.substring(start + 1, end)
if(start !== -1 && end !== -1){
return reverseInParentheses(arr.substring(0, start) +
[...temp].reverse().join('') +
arr.substring(end + 1))
}
return arr
}
console .log (reverseInParentheses('(bar)'))
console .log (reverseInParentheses('foo(bar)baz'))
console .log (reverseInParentheses('foo(bar(baz))blim'))
console .log (reverseInParentheses('((see)(you))'))
I don't particularly like this, combining the iteration to find the parentheses with recursion to keep reapplying the function until there are none left. It feels awkward.
There are other solutions, as you noted. One would be to use regular expressions. Note that the language of balanced parentheses is not a regular language, and hence cannot be captured by any one regular expression, but you can repeatedly apply regular expression operations in an iteration or a recursion to get this to work. Here is one version of that.
const rev = ([...cs]) => cs.reverse().join('')
const reverseInParentheses = (s) =>
/\(([^)]*)\)/ .test (s)
? reverseInParentheses (s .replace(/(.*)\(([^)]*)\)(.*)/, (_, a, b, c) => a + rev(b) + c))
: s
console .log (reverseInParentheses('(bar)'))
console .log (reverseInParentheses('foo(bar)baz'))
console .log (reverseInParentheses('foo(bar(baz))blim'))
console .log (reverseInParentheses('((see)(you))'))
Briefly, this finds innermost pairs of parentheses, replaces them with the reversal of their content, then recurs on the result, bottoming out when there are no more pairs found.
This solution was thrown together, and there are probably better regular expressions operations available.
But I actually prefer a different approach altogether, treating the characters of the string as events for a simple state machine, with a stack of nested parenthesized substrings. Here is what I wrote:
const reverseInParentheses = ([c, ...cs], res = ['']) =>
c == undefined
? res [0]
: c == '('
? reverseInParentheses (cs, [...res, ''])
: c == ')'
? reverseInParentheses (cs, [...res.slice(0, -2), res[res.length - 2] + [...res[res.length - 1]].reverse().join('')])
: reverseInParentheses (cs, [...res.slice(0, -1), res[res.length - 1] + c])
console .log (reverseInParentheses('(bar)'))
console .log (reverseInParentheses('foo(bar)baz'))
console .log (reverseInParentheses('foo(bar(baz))blim'))
console .log (reverseInParentheses('((see)(you))'))
We can examine the behavior by adding this as the first line of the body expression:
console .log (`c: ${c ? `"${c}"` : '< >'}, cs: "${cs.join('')}", res: ["${res.join('", "')}"]`) ||
For '((see)(you))', we would get something like this:
curr (c)
remaining (cs)
stack (res)
"("
"(see)(you))"
[""]
"("
"see)(you))"
["", ""]
"s"
"ee)(you))"
["", "", ""]
"e"
"e)(you))"
["", "", "s"]
"e"
")(you))"
["", "", "se"]
")"
"(you))"
["", "", "see"]
"("
"you))"
["", "ees"]
"y"
"ou))"
["", "ees", ""]
"o"
"u))"
["", "ees", "y"]
"u"
"))"
["", "ees", "yo"]
")"
")"
["", "ees", "you"]
")"
""
["", "eesuoy"]
< >
< >
["yousee"]
I choose to process this state machine recursively, because I prefer working with immutable data, not reassigning variables, etc. But this technique should work equally well with an iterative approach.
String reverseInParentheses(String inputString) {
//recursion
int start = -1;
int end = -1 ;
for(int i = 0; i < inputString.length(); i++){
if(inputString.charAt(i) == '('){
start = i;
}
if(inputString.charAt(i) == ')'){
end = i;
String reverse = new StringBuilder(inputString.substring(start+1, end)).reverse().toString();
return reverseInParentheses(inputString.substring(0, start) + reverse+ inputString.substring(end+1));
}
}
return inputString;
}
function solution(inputString) {
let s;
let e = 0;
while (e < inputString.length) {
//if we saw a ')', we mark the index as e, then we go back the
//nearest '(', and mark the index as s
if (inputString[e] === ')') {
s = e;
while (inputString[s] !== '(') {
s--;
}
//get the string in the parenthesis
let beforeRevert = inputString.slice(s + 1, e);
//revert it
let reversed = beforeRevert.split('').reverse().join('');
//put pieces together to get a new inputString
inputString = inputString.slice(0, s) + reversed +
inputString.slice(e + 1, inputString.length)
//because we get rid of the '(' and ')', now we are at index e-1 of
//new inputString
e--;
} else {
e++;
}
}
return inputString;
}
You could try this. It worked for me.
function solution(s) {
while (true) {
let c = s.indexOf(")");
if (c === -1) {
break;
}
let o = s.substring(0, c).lastIndexOf("(");
let start = s.substring(0, o);
let middle = s.substring(o + 1, c).split("").reverse().join("");
let end = s.substring(c + 1, s.length);
s = start + middle + end;
}
return s;
}
Related
If I have a string a12c56a1b5 then out put should be a13b5c56 as character a is repeated twice so a12 becomes a13
I have tried this:
function stringCompression (str) {
var output = '';
var count = 0;
for (var i = 0; i < str.length; i++) {
count++;
if (str[i] != str[i+1]) {
output += str[i] + count;
count = 0;
}
}
console.log(output); // but it returns `a11121c15161a111b151` instead of `a13b5c56`
}
It is happening because the code is counting the occurrence of each element and appending it, even the numbers in the string.
In this code,
for (var i = 0; i < str.length; i++) {
count++;
if (str[i] != str[i+1]) {
output += str[i] + count;
count = 0;
}
}
in first iteration i = 0, str[i] = 'a' and str[i + 1] = '1' for the given string a12c56a1b5 which are not equal hence, it will generate the output as a1 for first iteration, then a111 for second iteration since str[i] = '1' and str[i + 1] = '2' now, and so on.
We can achieve this by first separating the characters from the count. Assuming, that there would be characters from a-z and A-Z only followed by the count. We can do something like this, str.match(/[a-zA-Z]+/g) to get the characters: ["a", "c", "a", "b"] and str.match(/[0-9]+/g) to get their counts: ["12", "56", "1", "5"], put them in an object one by one and add if it already exists.
Something like this:
function stringCompression(str) {
var characters = str.match(/[a-zA-Z]+/g);
var counts = str.match(/[0-9]+/g);
var countMap = {};
for (var i = 0; i < characters.length; i++) {
if (countMap[characters[i]]) {
countMap[characters[i]] += parseInt(counts[i]);
} else {
countMap[characters[i]] = parseInt(counts[i]);
}
}
var output = Object.keys(countMap)
.map(key => key + countMap[key])
.reduce((a, b) => a + b);
console.log(output);
}
stringCompression('a12c56a1b5')
Using regex to extract word characters and numbers. Keeps an object map res to track and sum up following numbers. sorts and converts back to a string.
As an example, the for-of loop iteration flow with str=a12c56a1b5:
c='a', n='12'
res['a'] = (+n = 12) + ( (res['a'] = undefined)||0 = 0)
or ie: res['a'] = 12 + 0
c='c', n='56'
res['c'] = 56 + 0
c='a', n='1'
res['a'] = 1 + (res['a'] = 12 from iteration 1.) = 13
c='b', n='5'
res['b'] = 5 + 0
thus res = { 'a': 13, 'c': 56, 'b': 5 } after the for-of loop finishes
function stringCompression (str) {
// build object map with sums of following numbers
const res = {}
for(const [,c,n] of str.matchAll(/(\w+)(\d+)/g))
res[c] = +n + (res[c]||0)
// convert object map back to string
output = Object.entries(res)
output.sort(([a],[b])=>a<b ? -1 : a>b ? 1 : 0)
output = output.map(([a,b])=>`${a}${b}`).join('')
console.log(output); // but it returns `a11121c15161a111b151` instead of `a13b5c56`
}
stringCompression('a12c56a1b5')
[,c,n] = [1,2,3] is equivalent to c=2, n=3. It is called destructuring.
matchAll matches on a regex. It's a relatively new shorthand for calling .exec repeatedly to execute a regular expression that collects all the results that the regular expression matches on.
(\w+)(\d+) is a regex for two capture groups,
\w+ is for one or more alpha characters, \d+ is for one or more digits.
for(const [,c,n] of str.matchAll...) is equivalent to:
for each M of str.matchAll...
const c = M[1], n = M[2]`
res[c]||0 is shorthand for:
"give me res[c] if it is truthy (not undefined, null or 0), otherwise give me 0"
+n uses the unary operator + to force an implicit conversion to a number. JavaScript specs for + unary makes it convert to number, since + unary only makes sense with numbers.
It is basically the same as using Number(n) to convert a string to an number.
Conversion back to a string:
Object.entries converts an object {"key":value} to an array in the form of [ [key1, value1], [key2, value2] ]. This allows manipulating the elements of an object like an array.
.sort sorts the array. I destructured the keys to sort on the keys, so "a" "b" "c" are kept in order.
.map takes an array, and "maps" it to another array. In this case I've mapped each [key,value] to a string key+value, and then taking the final mapped array of key+value strings and joined them together to get the final output.
In case it asks you to sort it alphabetically, I added #user120242's sorting code snippet to #saheb's entire answer (in between Object.keys(countMap) and .map(...). That worked for me. I tried using #user120242's whole answer, but it did not pass all the tests since it did not add the repeated letters for longer strings. But #user120242's answer did work. It just need to be sorted alphabetically and it passed all the test cases in HackerRank. I had this question for a coding assessment (called "Better Coding Compression").
P.S. I also removed checking the capital letters from #saheb's code since that wasn't required for my coding challenge.
Here's how mine looked like:
function stringCompression(str) {
var characters = str.match(/[a-zA-Z]+/g);
var counts = str.match(/[0-9]+/g);
var countMap = {};
for (var i = 0; i < characters.length; i++) {
if (countMap[characters[i]]) {
countMap[characters[i]] += parseInt(counts[i]);
} else {
countMap[characters[i]] = parseInt(counts[i]);
}
}
var output = Object.keys(countMap)
.sort(([a],[b])=>a<b ? -1 : a>b ? 1 : 0)
.map(key => key + countMap[key])
.reduce((a, b) => a + b);
console.log(output);
}
stringCompression('a12c56a1b5')
I want to identify strings that are made up exclusively of same-length character groups. Each one of these groups consists of at least two identical characters. So, here are some examples:
aabbcc true
abbccaa false
xxxrrrruuu false (too many r's)
xxxxxfffff true
aa true (shortest possible positive example)
aabbbbcc true // I added this later to clarify my intention
#ilkkachu: Thanks for your remark concerning the repetition of the same character group. I added the example above. Yes, I want the last sample to be tested as true: a string made up of the two letter groups aa, bb, bb, cc.
Is there a simple way to apply this condition-check on a string using regular expressions and JavaScript?
My first attempt was to do something like
var strarr=['aabbcc','abbccaa','xxxrrrruuu',
'xxxxxfffff','aa','negative'];
var rx=/^((.)\2+)+$/;
console.log(strarr.map(str=>str+': '+!!str.match(rx)).join('\n'));
It does look for groups of repeated characters but does not yet pay attention to these groups all being of the same length, as the output shows:
aabbcc: true
abbccaa: false
xxxrrrruuu: true // should be false!
xxxxxfffff: true
aa: true
aabbbbcc: true
negative: false
How do I get the check to look for same-length character groups?
To get all the groups of the same character has an easy regex solution:
/(.)\1*/g
Just repeating the backreference \1 of the character in capture group 1.
Then just check if there's a length in the array of same character strings that doesn't match up.
Example snippet:
function sameLengthCharGroups(str)
{
if(!str) return false;
let arr = str.match(/(.)\1*/g) //array with same character strings
.map(function(x){return x.length}); //array with lengths
let smallest_length = arr.reduce(function(x,y){return x < y ? x : y});
if(smallest_length === 1) return false;
return arr.some(function(n){return (n % smallest_length) !== 0}) == false;
}
console.log("-- Should be true :");
let arr = ['aabbcc','xxxxxfffff','aa'];
arr.forEach(function(s){console.log(sameLengthCharGroups(s)+' : '+ s)});
console.log("-- Should also be true :");
arr = ['aabbbbcc','224444','444422',
'666666224444666666','666666444422','999999999666666333'];
arr.forEach(function(s){console.log(sameLengthCharGroups(s)+' : '+ s)});
console.log("-- Should be false :");
arr = ['abbcc','xxxrrrruuu','a','ab','',undefined];
arr.forEach(function(s){console.log(sameLengthCharGroups(s)+' : '+ s)});
ECMAScript 6 version with fat arrows (doesn't work in IE)
function sameLengthCharGroups(str)
{
if(!str) return false;
let arr = str.match(/(.)\1*/g)
.map((x) => x.length);
let smallest_length = arr.reduce((x,y) => x < y ? x : y);
if(smallest_length === 1) return false;
return arr.some((n) => (n % smallest_length) !== 0) == false;
}
Or using exec instead of match, which should be faster for huge strings.
Since it can exit the while loop as soon a different length is found.
But this has the disadvantage that this way it can't get the minimum length of ALL the lengths before comparing them.
So those with the minimum length at the end can't be found as OK this way.
function sameLengthCharGroups(str)
{
if(!str) return false;
const re = /(.)\1*/g;
let m, smallest_length;
while(m = re.exec(str)){
if(m.index === 0) {smallest_length = m[0].length}
if(smallest_length > m[0].length && smallest_length % m[0].length === 0){smallest_length = m[0].length}
if(m[0].length === 1 ||
// m[0].length !== smallest_length
(m[0].length % smallest_length) !== 0
) return false;
}
return true;
}
console.log("-- Should be true :");
let arr = ['aabbcc','xxxxxfffff','aa'];
arr.forEach(function(s){console.log(sameLengthCharGroups(s)+' : '+ s)});
console.log("-- Should also be true :");
arr = ['aabbbbcc','224444','444422',
'666666224444666666','666666444422','999999999666666333'];
arr.forEach(function(s){console.log(sameLengthCharGroups(s)+' : '+ s)});
console.log("-- Should be false :");
arr = ['abbcc','xxxrrrruuu','a','ab','',undefined];
arr.forEach(function(s){console.log(sameLengthCharGroups(s)+' : '+ s)});
Here's one that runs in linear time:
function test(str) {
if (str.length === 0) return true;
let lastChar = str.charAt(0);
let seqLength = 1;
let lastSeqLength = null;
for (let i = 1; i < str.length; i++) {
if (str.charAt(i) === lastChar) {
seqLength++;
}
else if (lastSeqLength === null || seqLength === lastSeqLength) {
lastSeqLength = seqLength;
seqLength = 1;
lastChar = str.charAt(i);
}
else {
return false;
}
}
return (lastSeqLength === null || lastSeqLength === seqLength);
}
Since requirements changed or weren't clear as now this is the third solution I'm posting. To accept strings that could be divided into smaller groups like aabbbb we could:
Find all lengths of all different characters which are 2 and 4 in this case.
Push them into an array named d.
Find the lowest length in set named m.
Check if all values in d have no remainder when divided by m
Demo
var words = ['aabbbcccdddd', 'abbccaa', 'xxxrrrruuu', 'xxxxxfffff', 'aab', 'aabbbbccc'];
words.forEach(w => {
var d = [], m = Number.MAX_SAFE_INTEGER;
var s = w.replace(/(.)\1+/gy, x => {
d.push(l = x.length);
if (l < m) m = l;
return '';
});
console.log(w + " => " + (s == '' && !d.some(n => n % m != 0)));
});
Using sticky flag y and replace method you could do this much more faster. This trick replaces occurrences of first one's length with an empty string (and stops as soon as an occurrence with different length happens) then checks if there are some characters left:
var words = ['aabbcc', 'abbccaa', 'xxxrrrruuu', 'xxxxxfffff', 'aa'];
words.forEach(w => {
console.log(w + " => " + (w.replace(/(.)\1+/gy, ($0, $1, o) => {
return $0.length == (o == 0 ? l = $0.length : l) ? '' : $0;
}).length < 1));
});
Another workaround would be using replace() along with test(). First one replaces different characters with their corresponding length and the second looks for same repeated numbers in preceding string:
var str = 'aabbc';
/^(\d+\n)\1*$/.test(str.replace(/(.)\1+/gy, x => x.length + '\n'));
Demo:
var words = ['aabbcc', 'abbccaa', 'xxxrrrruuu', 'xxxxxfffff', 'aa'];
words.forEach(w =>
console.log(/^(\d+\n)\1*$/.test(w.replace(/(.)\1+/gy, x => x.length + '\n')))
);
Since regex has never been my forte here's an approach using String#replace() to add delimiter to string at change of letter and then use that to split into array and check that all elements in array have same length
const values = ['aabbcc', 'abbccaa', 'xxxrrrruuu', 'xxxxxfffff', 'aa'];
const expect = [true, false, false, true, true];
const hasMatchingGroups = (str) => {
if(!str || str.length %2) return false;
const groups = str.replace(/[a-z]/g,(match, offset, string) => {
return string[offset + 1] && match !== string[offset + 1] ? match + '|' : match;
}).split('|');
return groups.every(s => s.length === groups[0].length)
}
values.forEach((s, i) => console.log(JSON.stringify([s,hasMatchingGroups(s), expect[i]])))
The length of the repeated pattern of same charcters needs to be specified within the regular expression. The following snippet creates regular expressions looking for string lengths of 11 down to 2. The for-loop is exited once a match is found and the function returns the length of the pattern found:
function pat1(s){
for (var i=10;i;i--)
if(RegExp('^((.)\\2{'+i+'})+$').exec(s))
return i+1;
return false;}
If nothing is found false is returned.
If the length of the pattern is not required, the regular expression can also be set up in one go (without the need of the for loop around it):
function pat2(s){
var rx=/^((.)\2)+$|^((.)\4{2})+$|^((.)\6{4})+$|^((.)\8{6})+$/;
return !!rx.exec(s);
}
Here are the results from both tests:
console.log(strarr.map(str=>
str+': '+pat1(str)
+' '+pat2(str)).join('\n')+'\n');
aabbcc: 2 true
abbccaa: false false
xxxrrrruuu: false false
xxxxxfffff: 5 true
aa: 2 true
aabbbbcc: 2 true
negative: false false
The regex in pat2 looks for certain repetition-counts only. When 1, 2, 4 or 6 repetitions of a previous character are found then the result is positive. The found patterns have lengths of 2,3,5 or 7 characters (prime numbers!). With these length-checks any pattern-length dividable by one of these numbers will be found as positive (2,3,4,5,6,7,8,9,10,12,14,15,16,18,20,21,22,24,...).
I'm new on this and javascript. I've tried to solve an exercise that consist to find repeated letter a in an array. The way to do is use basic structures (no regex neither newer ways of javascript (only ES5)). I need to do it this way to understand the bases of the language.
The output must be this:
//Captain America, the letter 'C' => 2 times.
//Captain America, the letter 'A' => 4 times.
//Captain America, the letter 'I' => 2 times.
I'm not looking for the solution, only the way to do it and its logical structures. Any suggestions are welcome.
My way but it doesn't work:
function duplicateLetter(name) {
var newArray = [];
for (var i=0; i<name.length; i++) {
console.log(name[i].indexOf(newArray));
if (name[i].indexOf(newArray) === 0) {
newArray.push(name[i]);
}
}
console.log(newArray);
//console.log(name + ", the letter '" + (newArray[0]).toUpperCase() + "' => " + newArray.length + " times");
}
duplicateLetter("Captain America");
function duplicateLetter(o) {
var arr = o.toUpperCase().split('');
var obj = {};
for(var v in arr) {
obj[arr[v]] = obj[arr[v]] || 0;
obj[arr[v]]++;
}
for(var v in obj) {
console.log(o + ", the letter '" + v + "' => " + obj[v] + ' times.');
}
}
duplicateLetter("Captain America");
The explanation:
We make the string upper case, then turn it into an array of letters.
We loop over the array, here, arr[v] becomes our letter, and:
If the key arr[v] doesn't exist in our object, we set it to 0.
We increment the value of the key arr[v] in our object (this causes obj['c'] to increment every time our letter is c. You can notice that this keeps track of the number of letters in our string.
We loop over the object v, printing the number of occurrences of each letter to console.
Note that this considers the space character as a letter. If you want an answer that doesn't, please specify so.
Here's a different answer that doesn't use objects and only counts letters (and not spaces or punctuation) to prove that everything is possible in more than one way.
// Not using objects and not counting anything but letters.
function duplicateLetter(o) {
var letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var arr = o.toUpperCase().split('');
var count = [];
for(var v in arr) {
pos = letters.indexOf(arr[v]);
if(pos < 0) continue; // It wasn't a letter.
count[pos] = count[pos] || 0;
count[pos]++;
}
for(var v in count) {
if(!(count[v] > 0)) continue; // The letter never appeared.
console.log(o + ", the letter '" + letters[v] + "' => " + count[v] + ' times.');
}
}
duplicateLetter("Captain America");
I could probably also attempt an answer that doesn't use arrays at all!
Edit:
You can use for(a in b) loops to iterate arrays as well as objects. This is because an array is really just an object in which all enumerable properties have integer indices:
arr = [10,15,"hi"] is almost the same as arr = {'0' : 10, '1' : 15, '2' : "hi"} in the way Javascript works internally. Therefore, for (v in arr) will iterate over the array normally.
As requested, the same answer with normal for loops:
// Not using objects and not counting anything but letters.
function duplicateLetter(o) {
var letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var arr = o.toUpperCase().split('');
var count = [];
for(var v = 0; v < arr.length; v++) {
pos = letters.indexOf(arr[v]);
if(pos < 0) continue; // It wasn't a letter.
count[pos] = count[pos] || 0;
count[pos]++;
}
for(var v = 0; v < count.length; v++) {
if(!(count[v] > 0)) continue; // The letter never appeared.
console.log(o + ", the letter '" + letters[v] + "' => " + count[v] + ' times.');
}
}
duplicateLetter("Captain America");
Note that nothing changed outside what was in the for brackets. The for-in notation is just easier for the human brain to comprehend, in my opinion, and is the reason I used it.
As for count[pos] = count[pos] || 0;, explaining why it works the way it does is extremely tedious, since it requires that you know precisely what the || operator does. So I'm just going to state what it does, without explaining it.
Basically, count[pos] = count[pos] || 0; is the same as:
if(count[pos]) { // If count[pos] evaluates to true.
count[pos] = count[pos]
} else { // count[pos] is false, '', null, undefined, 0, or any other value that evaluates to false.
count[pos] = 0;
}
Note that this works because at the start, count[pos] is undefined (count is an empty array), so it puts a 0 in it. If we find the letter again, count[pos] is defined, and is a positive value, and therefore evaluates to true, so we don't change it.
Just consider a = a || b to be equal to:
Put the default value of b into a if a is undefined (or evaluates to false by any other means).`
Make an object whose keys are the letters and values are the number of times that letter was repeated. For example,
'abbc' => {'a': 1, 'b': 2, 'c': 1}
I am trying to make a function that loops through a word, identifies the first vowel found (if any) in the word, and then splits up the word after the vowel.
example input: 'super'
example output: 'su', 'per'
function consAy(word){
if(word[i].indexOf("a" >= 0) || word[i].indexOf("e" >= 0) || word[i].indexOf("i" >= 0) || word[i].indexOf("o" >= 0) || word[i].indexOf("u" >= 0)){
}
One way to do it is to use a regular expression to .match() the pattern you are looking for:
function consAy(word){
var result = word.match(/^([^aeiou]*[aeiou])(.+)$/i)
return result ? result.slice(1) : [word]
}
console.log( consAy('super') )
console.log( consAy('AMAZING') )
console.log( consAy('hi') )
console.log( consAy('why') )
The function I've shown returns an array. If there was a vowel that was not at the end then the array has two elements. If there was only a vowel at the end, or no vowel, then the array has one element that is the same as the input string.
A brief breakdown of the regex /^([^aeiou]*[aeiou])(.+)$/i:
^ // beginning of string
[^aeiou]* // match zero or more non-vowels
[aeiou] // match any vowel
.+ // match one or more of any character
$ // end of string
...where the parentheses are used to create capturing groups for the two parts of the string we want to separate, and the i after the / makes it case insensitive.
The .match() method returns null if there was no match, so that's what the ternary ?: expression is for. You can tweak that part if you want a different return value for the case where there was no match.
EDIT: I was asked for a non-regex solution. Here's one:
function consAy(word){
// loop from first to second-last character - don't bother testing the last
// character, because even if it's a vowel there are no letters after it
for (var i = 0; i < word.length - 1; i++) {
if ('aeiou'.indexOf(word[i].toLowerCase()) != -1) {
return [word.slice(0, i + 1), word.slice(i + 1)]
}
}
return [word]
}
console.log( consAy('super') )
console.log( consAy('AMAZING') )
console.log( consAy('hi') )
console.log( consAy('why') )
This assumes a reasonably modern browser, or Node.
const string = "FGHIJK";
const isVowel = c => c.match(/[AEIOU]/i);
const pos = [...string].findIndex(isVowel);
const truncatedString = `${[...string].slice(0, pos + 1)}`;
truncatedString; // "FGHI"
Edit
As has been pointed out, the above is significantly more hassle than it's worth. Without further ado, a much saner approach.
const string = "FGHIJK";
const vowels = /[aeiou]/i;
const truncateAfter = (string, marker) => {
const pos = string.search(marker);
const inString = pos >= 0;
return string.slice(0, inString ? pos : string.length);
};
const truncated = truncateAfter(string, vowels);
Without using a RegEx of any kind. Ye ole fashioned algorithm.
const truncateAfter = (string, markers) => {
let c = "";
let buffer = "";
for (let i = 0, l = string.length; i < l; i += 1) {
c = string[i];
buffer += c;
if (markers.includes(c)) {
break;
}
}
return buffer;
};
const truncatedString = truncateAfter(
"XYZABC",
["A", "E", "I", "O", "U"],
);
With RegEx golf.
const string = "BCDEFG";
const truncatedString = string.replace(/([aeiou]).*/i, "$1");
With a reduction.
const isVowel = c => /[aeiou]/i.test(c);
const last = str => str[str.length - 1];
const truncatedString = [...string].reduce((buffer, c) =>
isVowel(last(buffer)) ? buffer : buffer + c, "");
Via a dirty filter hack, that takes way too much power O(n**2).
const truncatedString = [...string]
.filter((_, i, arr) => i <= arr.search(/[aeiou]/i));
There are others, but I think that's enough to shake the cobwebs out of my brain, for now.
I always like to take opportunities to write incomprehensible array-based code, so with that in mind...
const regexMatcher = pattern => input => {
return input.match(pattern)
};
const splitAtFirstMatch = matcher => arrayLike => {
return [...arrayLike]
.reduce(([pre, post, matchFound], element) => {
const addPre = matchFound || matcher(element);
return [
matchFound ? pre :[...pre, element],
matchFound ? [...post, element] : post,
addPre
];
}, [[],[], false])
.slice(0, 2)
.map(resultArrays => resultArrays.join(''));
};
console.log(splitAtFirstMatch(regexMatcher(/[aeiou]/))('super'));
I've looked this up online without much results because it's quite hard to describe in a few words.
Basically, I need to have a function, say puntil which takes the argument string. Basically, the function permutes until the string is equal to the argument.
For example if you run puntil('ab') it should do inside the function:
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
aa
ab !! MATCH
Another example, for puntil('abcd') it will do
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
aa
ab
ac
ad
ae
af
ag
ah
ai
aj
ak
al
am
an
ao
ap
aq
ar
as
at
au
av
aw
ax
ay
az
... etc etc ..
until it matches abcd.
Basically an infinite permutation until it matches.
Any ideas?
Here is the fiddle
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'];
var output = "";
var match = "cccc"; //<----------- match here
//This is your main function
function permutate() {
var d = 0; // d for depth
while (true) {
//Your main alphabet iteration
for (var i=0; i<alphabet.length; i++){
//First level
if (d === 0) {
console.log(alphabet[i])
output = alphabet[i];
}
else
iterator(alphabet[i], d); //Call iterator for d > 0
if (output === match)
return;
}
d++; //Increase the depth
}
}
//This function runs n depths of iterations
function iterator(s, depth){
if (depth === 0)
return;
for (var i=0; i<alphabet.length; i++){
if (depth > 1)
iterator(s + alphabet[i], depth - 1)
else {
console.log(s + alphabet[i]);
output = s + alphabet[i];
}
if (output === match)
return;
}
};
Explanation:
Your program needs to traverse a tree of alphabet like this
[a]
-[a]
-[a]
-[a]...
-[b]...
[b] ...
-[b] -
-[a]...
-[b]...
[b] - ...
[c] - ...
This could have easily been done through a conventional recursive function if not for the requirement that you need to finish each depth first.
So we need a special iterator(s, depth) function which can perform number of nested iterations (depth) requested.
So the main function can call the iterator with increasing depths (d++).
That's all!!
Warning: This is a prototype only. This can be optimized and improved. It uses global variables for the ease of demonstrating. Your real program should avoid globals. Also I recommend calling the iterator() inside setTimeout if your match word is too long.
The n depths can only be limited by your resources.
Fiddle here
function next(charArray, rightBound){
if(!rightBound){
rightBound = charArray.length;
}
var oldValue = charArray[rightBound-1];
var newValue = nextCharacter(charArray[rightBound-1]);
charArray[rightBound-1] = newValue;
if(newValue < oldValue){
if(rightBound > 1){
next(charArray, rightBound-1);
}
else{
charArray.push('a');
}
}
return charArray;
}
function nextCharacter(char){
if(char === 'z'){
return 'a'
}
else{
return String.fromCharCode(char.charCodeAt(0) + 1)
}
}
function permuteUntil(word){
var charArray = ['a'];
var wordChain = ['a'];
while(next(charArray).join('') !== word){
wordChain.push(charArray.join(''));
}
wordChain.push(word);
return wordChain.join(', ');
}
alert(permuteUntil('ab'));
What OP is asking is a bit ambiguous, so I'll post for both the things (that I doubt) OP is asking.
First, the question can be, what will be the position of input string in the infinite permutation of alphabets (which I see as more legit question, I've given the reason later). This can be done in the following manner:
Taking an example (input = dhca). So, all strings of 1 to 3 characters length will come before this string. So, add: 26^1 + 26^2 + 26^3 to the answer. Then, 1st character is d, which means, following the dictionary, if 1st character is a | b | c, all characters past that are valid. So, add 3 * 26^3 to the answer. Now, say 1st character is d. Then, we can have all characters from a to g (7) and last 2 characters can be anything. So, add 7 * 26^2 to the answer. Going on in this way, we get the answer as:
26^1 + 26^2 + 26^3 + (3 * 26^3) + (7 * 26^2) + (2 * 26^1) + (0) + 1
= 75791
OK. Now the second thing, which I think OP is actually asking (to print all strings before we get a match). Now, why I think this is unfeasible is because if we have input as zzzzz (5 characters long) we need to print 26^1 + 26^2 + 26^3 + 26^4 + 26^5 strings, which is 12356630. So, for this part, I assume max length of input string is 5 (And definitely no more) because for 6 character length string, we need to print ~321272406 strings --> NOT POSSIBLE.
So, a simple solution to this can be:
Create an array of size 27 as: arr[] = {'', 'a', 'b', ..., 'y', 'z'}. 1st character is null.
Write 5 (max string length) nested loops from 0 to 26 (inclusive) and add it to dummy string and print it. Something like.
for i from 0 to 26
String a = "" + arr[i]
for j from 0 to 26
String b = a + arr[j]
for k from 0 to 26
String c = b + arr[k]
for l from 0 to 26
String d = c + arr[l]
for m from 0 to 26
String e = d + arr[m]
print e
if(e equals input string)
break from all loops //use some flag, goto statement etc.
You asked for a more elegant solution, here's a simple function that converts integers into lowercase strings of characters allowing you to easily iterate through strings.
function toWord(val, first, out) {
if(first == 1)
out = String.fromCharCode(97 + val % 26) + out;
else
out = String.fromCharCode(97 + (val-1) % 26) + out;
if(val % 26 == 0 && first == 0) {
val -= 26;
}
else {
val -= val %26;
}
val = val / 26;
if(val != 0)
return toWord(val, 0, out);
else
return out;
}
It's by no means perfect, but it's short and simple. When calling the function set val to be the integer you want to convert, first as 1, and out as "".
For example the following will apply yourFunction to the first 10,000 lowercase strings
for(int i=0; i<10000; i++) {
youFunction(toWord(i, 1, ""));
}
So you need to always start incrementing from a? Since they are char values you can easily do this with the following construct:
Note that this is a java solution :)
public static char[] increment(char[] arr, int pos) {
// get current position char
char currentChar = arr[pos];
// increment it by one
currentChar++;
// if it is at the end of it's range
boolean overflow = false;
if (currentChar > 'Z') {
overflow = true;
currentChar = 'A';
}
// always update current char
arr[pos] = currentChar;
if (overflow) {
if (pos == 0) {
// resize array and add new character
char[] newArr = new char[arr.length + 1];
System.arraycopy(arr, 0, newArr, 0, arr.length);
newArr[arr.length] = 'A';
arr = newArr;
} else {
// overflowed, increment one position back
arr = increment(arr, pos - 1);
}
}
return arr;
}
public static void main(String[] args) {
final String stringToUse = "A";
final String stringToSearch = "ABCD";
char[] use = stringToUse.toCharArray();
for (;;) {
final String result = new String(use);
System.out.println("Candidate: " + result);
if (stringToSearch.equals(result)) {
System.out.println("Found!");
break;
}
use = increment(use, use.length - 1);
}
}