I need to reverse a string except the characters inside of "{}". I know how to reverse a string but I'm not sure how to create the exception. Please help.
function reverseChar(string2){
let string2Array = string2.split('');
let newArray = [];
for(let x = string2Array.length-1; x >= 0; x--){
newArray.push(string2Array[x])
}
console.log(newArray)
}
reverseChar("ab{cd}efg")
reverseChar("ab{cd}ef{gh}i")
Or, maybe, this is what you want?
function reverse(str) {
return str.split("").reverse().join("").replace(/}\w+\{/g,a=>reverse(a))
}
console.log(reverse("ab{cd}efg"))
console.log(reverse("ab{cd}ef{gh}i"))
The RegExp /}\w+\{/g will find any string of characters and numbers (\w+) that is enclosed by } and {. These patterns will exist after the whole string is reverse()-d initially. In the callback function to the String.replace() method the matched string will then be reversed again.
You can try this logic:
Get all the parts
If the part does not have special character, reverse it and set it.
Reverse the parts array
Join all the parts back and return it
function reverseChar(string2) {
const regex = /(\w+(?=\{|$)|\{\w+\})/g
return string2.match(regex)
.map((str) => /\{/.test(str) ? str : str.split("").reverse().join(""))
.reverse()
.join("")
}
console.log(reverseChar("ab{cd}efg"))
console.log(reverseChar("ab{cd}ef{gh}i"))
Related
I am able to use a for loop without using a helper method to reverse the string. But, how do I maintain the original order, space, and punctuation on the string?
Without using the reverse() helper method I am able to reverse the string but I cannot maintain the order of the words and punctuations.
// Reverse preserving the order, punctuation without using a helper
function reverseWordsPreserveOrder(words) {
let reverse = '';
for (let i = words.length -1; i >= 0; i--) {
reverse += words[i];
}
return reverse;
}
console.log(reverseWordsPreserveOrder('Javascript, can be challenging.'))
// output-> .gnignellahc eb nac ,tpircsavaJ
I expect the result to be like this:
// output-> tpircsavaJ, nac eb gnignellahc.
I'd use a regular expression and a replacer function instead: match consecutive word characters with \w+, and in the replacer function, use your for loop to reverse the substring, and return it:
function reverseSingleWord(word) {
let reverse = '';
for (let i = word.length -1; i >= 0; i--) {
reverse += word[i];
}
return reverse;
}
const reverseWordsPreserveOrder = str => str.replace(/\w+/g, reverseSingleWord);
console.log(reverseWordsPreserveOrder('Javascript, can be challenging.'))
If you are trying to do it manually — no reverse() of regexs, you could:
• Defined what you mean by punctuation. This can just be a set, or using an ascii range for letters, etc. But somehow you need to be able to tell letters from non letters.
• Maintain a cache of the current word because you are not reversing the whole sentence, just the words so you need to treat them individually.
With that you can loop through once with something like:
function reverseWordsPreserveOrder(s){
// some way to know what is letter and what is punt
let punct = new Set([',',' ', '.', '?'])
// current word reversed
let word = ''
// sentence so far
let sent = ''
for (let l of s){
if (punct.has(l)) {
sent += word + l
word = ''
} else {
word = l + word
}
}
sent += word
return sent
}
console.log(reverseWordsPreserveOrder('Javascript, can be challenging.'))
Having said this, it's probably more efficient to use a regex.
If you are only averse to reverse because you think it can't do the job, here is a more semantic version (based on #CertainPerformance's), in ES6 you can use the spread syntax (...) with the word string (as strings are iterable):
function reverseSingleWord(word) {
return [...word].reverse().join('');
}
const reverseWordsPreserveOrder = str => str.replace(/\w+/g, reverseSingleWord);
console.log(reverseWordsPreserveOrder('Javascript, can be challenging.'))
I want to remove all consonants in a string before the occurrence of a vowel and then replace it with an 'r'.
This means that 'scooby' will become 'rooby', 'xylographer' will become 'rographer' and so on. This is the algorithm I came up with:
1. Check if input type is not a string.
2. Use a variable(newStr) to hold lowercase conversion and splitted word.
3. Declare a variable(arrWord) to hold the length of the array.
4. Another variable called regex to check if a string starts with a consonant
5. Variable newArr holds the final result.
6. Search through the array, if the string does not start with a consonant
join it and return it.
7. Else keep looking for where the first vowel occurs in the word.
8. When found, remove all characters(consonants) before the vowel occurence
and replace them with an r.
9. Join the array together.
I have been able to come up with this:
const scoobyDoo = str => {
if(typeof str !== 'string'){
return 'This function accepts strings only';
}
let newStr = str.toLowerCase().split('');
let arrWord = newStr.length;
let regex = /[aeiou]/gi;
for (let i = 0; i < arrWord; i++){
if (newStr[0].match(regex)) {
let nothing = newStr.join('');
return nothing;
}
else {
let vowelIndex = newStr.indexOf(str.match(regex)[0]);
newStr.splice(0, vowelIndex, 'r');
return newStr.join('');
}
}
}
console.log(scoobyDoo('scOoby'));
I tested out the program again by capitalizing the first vowel index and instead of 'rooby' I get 'rscooby'. Why is that so?
Can you once try with following code in your else and see the changes
else {
var vowelIndex = newStr.indexOf(str.match(regex)[0]);
newStr.splice(0, vowelIndex, 'r');
return newStr.join("");
}
Is it not much easier like this? Or am I missing something??
'xylographer'.replace(/^\s*[^aieou]+(?=\w)/i,function(m,o,s){return "r"})
//"rographer"
'scooby'.replace(/^\s*[^aieou]+(?=\w)/i,function(m,o,s){return "r"})
//"rooby"
you could just use one reg expression for the whole algorithm and no need to split your string no more.
regexp to use.
/^[^aouie, AOUIE]+(?=[aouie, AOUIE])/g
of course you can readjust regexp to suit you more but this will get your main requirement.
On the line immediately after the else statement, I just called .toLowerCase() on it and it was fixed:
let vowelIndex = newStr.indexOf(str.match(regex)[0].toLowerCase());
I like keeping my code simple and readable
function pattern(str){
var vowelIndex = str.indexOf(str.match(/[aeiou]/)); //returns index of first vowel in str
return "r" + str.slice(vowelIndex);
}
console.log(pattern('xylographer'));
Very new to javascript so bear with me...
I need to check one element of an array(arr[1]), which contains a string, against another element of the same array(arr[0]) to determine if any letters included in element arr[1] are included in arr[0]. Those letters can be in any order, upper or lower case, and don't have to occur the same number of times (i.e. arr[0]="hheyyy" and arr[1]="hey" is fine). This is what i have (which works) but I was curious if anyone has a better/more simple way of doing this? -thanks in advance.
function mutation(arr) {
//splits the array into two separate arrays of individual letters
var newArr0 = arr.join('').toLowerCase().split('').slice(0,arr[0].length);
var newArr1 = arr.join('').toLowerCase().split('').slice(arr[0].length);
var boolArr = [];
//checks each letter of arr1 to see if it is included in any letter of arr0
for(var i = 0; i < newArr1.length; i++)
boolArr.push(newArr0.includes(newArr1[i]));
//results are pushed into an array of boolean values
if (boolArr.indexOf(false) !==-1)
return false; //if any of those values are false return false
else return true;
}
mutation(["hello", "hey"]); //returns false
You could use a regular expression:
function mutationReg(arr) {
return !arr[1].replace(new RegExp('['+arr[0].replace(/(.)/g,'\\\\$1')+']', "gi"), '').length;
}
This escapes every character in the second string with backslash (so it cannot conflict with regular expression syntax), surrounds it with square brackets, and uses that as a search pattern on the first string. Any matches (case-insensitive) are removed from the result, so that only characters are left over that don't occur in the second string. The length of the result is thus an indication on whether there was success or not. Applying the ! to it gives the correct boolean result.
This might not be the fastest solution.
Here is another ES6 alternative using a Set for good performance:
function mutation(arr) {
var chars = new Set([...arr[0].toLowerCase()]);
return [...arr[1].toLowerCase()].every (c => chars.has(c));
}
You can use Array.from() to convert string to an array, Array.prototype.every(), String.prototype.indexOf() to check if every charactcer in string converted to array is contained in string of other array element.
var arr = ["abc", "cab"];
var bool = Array.from(arr[0]).every(el => arr[1].indexOf(el) > -1);
console.log(bool);
I know JavaScript regular expressions have native lookaheads but not lookbehinds.
I want to split a string at points either beginning with any member of one set of characters or ending with any member of another set of characters.
Split before ເ, ແ, ໂ, ໃ, ໄ. Split after ະ.
In: ເລື້ອຍໆມະຫັດສະຈັນເອກອັກຄະລັດຖະທູດ
Out: ເລື້ອຍໆມະ ຫັດສະ ຈັນ ເອກອັກຄະ ລັດຖະ ທູດ
I can achieve the "split before" part using zero-width lookahead:
'ເລື້ອຍໆມະຫັດສະຈັນເອກອັກຄະລັດຖະທູດ'.split(/(?=[ໃໄໂເແ])/)
["ເລື້ອຍໆມະຫັດສະຈັນ", "ເອກອັກຄະລັດຖະທູດ"]
But I can't think of a general approach to simulating zero-width lookbehind
I'm splitting strings of arbitrary Unicode text so don't want to substitute in special markers in a first pass, since I can't guarantee the absence of any string from my input.
Instead of spliting, you may consider using the match() method.
var s = 'ເລື້ອຍໆມະຫັດສະຈັນເອກອັກຄະລັດຖະທູດ',
r = s.match(/(?:(?!ະ).)+?(?:ະ|(?=[ໃໄໂເແ]|$))/g);
console.log(r); //=> [ 'ເລື້ອຍໆມະ', 'ຫັດສະ', 'ຈັນ', 'ເອກອັກຄະ', 'ລັດຖະ', 'ທູດ' ]
You could try matching rather than splitting,
> var re = /((?:(?!ະ).)+(?:ະ|$))/g;
undefined
> var str = "ເລື້ອຍໆມະຫັດສະຈັນເອກອັກຄະລັດຖະທູດ"
undefined
> var m;
undefined
> while ((m = re.exec(str)) != null) {
... console.log(m[1]);
... }
ເລື້ອຍໆມະ
ຫັດສະ
ຈັນເອກອັກຄະ
ລັດຖະ
ທູດ
Then again split the elements in the array using lookahead.
If you use parentheses in the delimited regex, the captured text is included in the returned array. So you can just split on /(ະ)/ and then concatenate each of the odd members of the resulting array to the preceding even member. Example:
"ເລື້ອຍໆມະຫັດສະຈັນເອກອັກຄະລັດຖະທູ".split(/(ະ)/).reduce(function(arr,str,index) {
if (index%2 == 0) {
arr.push(str);
} else {
arr[arr.length-1] += str
};
return arr;
},[])
Result: ["ເລື້ອຍໆມະ", "ຫັດສະ", "ຈັນເອກອັກຄະ", "ລັດຖະ", "ທູ"]
You can do another pass to split on the lookahead:
"ເລື້ອຍໆມະຫັດສະຈັນເອກອັກຄະລັດຖະທູ".split(/(ະ)/).reduce(function(arr,str,index) {
if (index%2 == 0) {
arr.push(str);
} else {
arr[arr.length-1] += str
};
return arr;
},[]).reduce(function(arr,str){return arr.concat(str.split(/(?=[ໃໄໂເແ])/));},[]);
Result: ["ເລື້ອຍໆມະ", "ຫັດສະ", "ຈັນ", "ເອກອັກຄະ", "ລັດຖະ", "ທູ"]
I'm trying to extract a substring from a file with JavaScript Regex. Here is a slice from the file :
DATE:20091201T220000
SUMMARY:Dad's birthday
the field I want to extract is "Summary". Here is the approach:
extractSummary : function(iCalContent) {
/*
input : iCal file content
return : Event summary
*/
var arr = iCalContent.match(/^SUMMARY\:(.)*$/g);
return(arr);
}
function extractSummary(iCalContent) {
var rx = /\nSUMMARY:(.*)\n/g;
var arr = rx.exec(iCalContent);
return arr[1];
}
You need these changes:
Put the * inside the parenthesis as
suggested above. Otherwise your matching
group will contain only one
character.
Get rid of the ^ and $. With the global option they match on start and end of the full string, rather than on start and end of lines. Match on explicit newlines instead.
I suppose you want the matching group (what's
inside the parenthesis) rather than
the full array? arr[0] is
the full match ("\nSUMMARY:...") and
the next indexes contain the group
matches.
String.match(regexp) is
supposed to return an array with the
matches. In my browser it doesn't (Safari on Mac returns only the full
match, not the groups), but
Regexp.exec(string) works.
You need to use the m flag:
multiline; treat beginning and end characters (^ and $) as working
over multiple lines (i.e., match the beginning or end of each line
(delimited by \n or \r), not only the very beginning or end of the
whole input string)
Also put the * in the right place:
"DATE:20091201T220000\r\nSUMMARY:Dad's birthday".match(/^SUMMARY\:(.*)$/gm);
//------------------------------------------------------------------^ ^
//-----------------------------------------------------------------------|
Your regular expression most likely wants to be
/\nSUMMARY:(.*)$/g
A helpful little trick I like to use is to default assign on match with an array.
var arr = iCalContent.match(/\nSUMMARY:(.*)$/g) || [""]; //could also use null for empty value
return arr[0];
This way you don't get annoying type errors when you go to use arr
This code works:
let str = "governance[string_i_want]";
let res = str.match(/[^governance\[](.*)[^\]]/g);
console.log(res);
res will equal "string_i_want". However, in this example res is still an array, so do not treat res like a string.
By grouping the characters I do not want, using [^string], and matching on what is between the brackets, the code extracts the string I want!
You can try it out here: https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_match_regexp
Good luck.
(.*) instead of (.)* would be a start. The latter will only capture the last character on the line.
Also, no need to escape the :.
You should use this :
var arr = iCalContent.match(/^SUMMARY\:(.)*$/g);
return(arr[0]);
this is how you can parse iCal files with javascript
function calParse(str) {
function parse() {
var obj = {};
while(str.length) {
var p = str.shift().split(":");
var k = p.shift(), p = p.join();
switch(k) {
case "BEGIN":
obj[p] = parse();
break;
case "END":
return obj;
default:
obj[k] = p;
}
}
return obj;
}
str = str.replace(/\n /g, " ").split("\n");
return parse().VCALENDAR;
}
example =
'BEGIN:VCALENDAR\n'+
'VERSION:2.0\n'+
'PRODID:-//hacksw/handcal//NONSGML v1.0//EN\n'+
'BEGIN:VEVENT\n'+
'DTSTART:19970714T170000Z\n'+
'DTEND:19970715T035959Z\n'+
'SUMMARY:Bastille Day Party\n'+
'END:VEVENT\n'+
'END:VCALENDAR\n'
cal = calParse(example);
alert(cal.VEVENT.SUMMARY);