Javascript regular expressions evaluating to both true and false - javascript

I'm trying to learn how to test regular expressions and I'm a little confused. Below is a test for a whether word letters are in a regular expression or not. If they are they should write to a substring, and if they are not (! and . and " ") they should dump to writing the previously recorded substring in an array. However, word.test(str) is equalling both false and true when testing in the loop! When I test it on it's own it evaluates fine (example after ****), but there is some weird thing going on where it doesn't work in a for loop. Any ideas?
function countWords(str) {
var testString = '';
var stringArray = [];
var retObj = {};
var word = /\S/;
for(i = 0; i<str.length; i++)
{
if (word.test(str[i]) === true);
{
testString = testString.concat(str[i]);
}
if (word.test(str[i]) === false);
{
stringArray = stringArray + "," + testString;
testString = "";
}
}
return stringArray;
}
console.log(countWords("Hi! How are you?"));
console.log("*****");
var phrase = "Hi! How are you?";
console.log(/\S/.test(phrase[3]));

if (word.test(str[i]) === true);
If the test is true, do nothing (;)
{
testString = testString.concat(str[i])
}
Then execute this compound statement.
if (word.test(str[i]) === false);
If the test is false, do nothing again (;).
{
stringArray = stringArray + "," + testString;
}
Then do this compound statement.
Your assignments are not consequences of the if statements, they're just freestanding, because of the extra semicolons.

Related

Palindrome Checker need a hand

I'm working on freeCodeCamp's Palindrome Checker. My code is a bit messy but it pretty works on every test except the nineth one. palindrome("almostomla") should return false but in my code it returns trueinstead. I think my nineth code has a little problem but couldn't solve that. I wonder where am I missing something.
function palindrome(str) {
let str1 = str.replace(/[^a-zA-Z\d:]/gi, '');
let str2 = str1.replace(/,/gi, '');
let str3 = str2.replace(/\./gi, '');
let str4 = str3.replace(/_/, "-");
let myStr = str4.toLowerCase(); //My string is ready for play
for (let i = 0; i < myStr.length; i++) {
if (myStr[i] != myStr[myStr.length - (i+1)]) { //I think there is a little mistake on this line
return false;
} else {
return true;
}
}
The problem is that you're only checking the first and last characters of the string. You should return true only after all iterations have finished:
function palindrome(str) {
let str1 = str.replace(/[^a-zA-Z\d:]/gi, '');
let str2 = str1.replace(/,/gi, '');
let str3 = str2.replace(/\./gi, '');
let str4 = str3.replace(/_/, "-");
let myStr = str4.toLowerCase(); //My string is ready for play
for (let i = 0; i < myStr.length; i++) {
if (myStr[i] != myStr[myStr.length - (i + 1)]) {
return false;
}
}
return true;
}
console.log(palindrome("almostomla"));
console.log(palindrome("foof"));
console.log(palindrome("fobof"));
console.log(palindrome("fobbf"));
Note that your initial regular expression is sufficient - it removes all characters that aren't alphabetical, numeric, or :, so the other 3 regular expressions you run later are superfluous. Since you're using the i flag, you can also remove the A-Z from the regex:
const stringToTest = str.replace(/[^a-z\d:]/gi, '');
It would also probably be easier just to .reverse() the string:
function palindrome(str) {
const strToTest = str.replace(/[^a-z\d:]/gi, '');
return strToTest.split('').reverse().join('') === strToTest;
}
console.log(palindrome("almostomla"));
console.log(palindrome("foof"));
console.log(palindrome("fobof"));
console.log(palindrome("fobbf"));

How to replace all same charter/string in text with different outcomes?

For example let's say I want to attach the index number of each 's' in a string to the 's's.
var str = "This is a simple string to test regex.";
var rm = str.match(/s/g);
for (let i = 0;i < rm.length ;i++) {
str = str.replace(rm[i],rm[i]+i);
}
console.log(str);
Output: This43210 is a simple string to test regex.
Expected output: This0 is1 a s2imple s3tring to tes4t regex.
I'd suggest, using replace():
let i = 0,
str = "This is a simple string to test regex.",
// result holds the resulting string after modification
// by String.prototype.replace(); here we use the
// anonymous callback function, with Arrow function
// syntax, and return the match (the 's' character)
// along with the index of that found character:
result = str.replace(/s/g, (match) => {
return match + i++;
});
console.log(result);
Corrected the code with the suggestion — in comments — from Ezra.
References:
Arrow functions.
"Regular expressions," from MDN.
String.prototype.replace().
For something like this, I would personally go with the split and test method. For example:
var str = "This is a simple string to test regex.";
var split = str.split(""); //Split out every char
var recombinedStr = "";
var count = 0;
for(let i = 0; i < split.length; i++) {
if(split[i] == "s") {
recombinedStr += split[i] + count;
count++;
} else {
recombinedStr += split[i];
}
}
console.log(recombinedStr);
A bit clunky, but works. It forgoes using regex statements though, so probably not exactly what you're looking for.

Swap Case on javascript

I made a script that changes the case, but result from using it on text is exactly the same text, without a single change. Can someone explain this?
var swapCase = function(letters){
for(var i = 0; i<letters.length; i++){
if(letters[i] === letters[i].toLowerCase()){
letters[i] = letters[i].toUpperCase();
}else {
letters[i] = letters[i].toLowerCase();
}
}
console.log(letters);
}
var text = 'So, today we have REALLY good day';
swapCase(text);
Like Ian said, you need to build a new string.
var swapCase = function(letters){
var newLetters = "";
for(var i = 0; i<letters.length; i++){
if(letters[i] === letters[i].toLowerCase()){
newLetters += letters[i].toUpperCase();
}else {
newLetters += letters[i].toLowerCase();
}
}
console.log(newLetters);
return newLetters;
}
var text = 'So, today we have REALLY good day';
var swappedText = swapCase(text); // "sO, TODAY WE HAVE really GOOD DAY"
You can use this simple solution.
var text = 'So, today we have REALLY good day';
var ans = text.split('').map(function(c){
return c === c.toUpperCase()
? c.toLowerCase()
: c.toUpperCase()
}).join('')
console.log(ans)
Using ES6
var text = 'So, today we have REALLY good day';
var ans = text.split('')
.map((c) =>
c === c.toUpperCase()
? c.toLowerCase()
: c.toUpperCase()
).join('')
console.log(ans)
guys! Get a little simplier code:
string.replace(/\w{1}/g, function(val){
return val === val.toLowerCase() ? val.toUpperCase() : val.toLowerCase();
});
Here is an alternative approach that uses bitwise XOR operator ^.
I feel this is more elegant than using toUppserCase/ toLowerCase methods
"So, today we have REALLY good day"
.split("")
.map((x) => /[A-z]/.test(x) ? String.fromCharCode(x.charCodeAt(0) ^ 32) : x)
.join("")
Explanation
So we first split array and then use map function to perform mutations on each char, we then join the array back together.
Inside the map function a RegEx tests if the value is an alphabet character: /[A-z]/.test(x) if it is then we use XOR operator ^ to shift bits. This is what inverts the casing of character. charCodeAt convert char to UTF-16 code. XOR (^) operator flips the char. String.fromCharCode converts code back to char.
If RegEx gives false (not an ABC char) then the ternary operator will return character as is.
References:
String.fromCharCode
charCodeAt
Bitwise operators
Ternary operator
Map function
One liner for short mode code wars:
let str = "hELLO wORLD"
str.split("").map(l=>l==l.toLowerCase()?l.toUpperCase():l.toLowerCase()).join("")
const swapCase = (myString) => {
let newString = ''; // Create new empty string
if (myString.match(/[a-zA-Z]/)) { // ensure the parameter actually has letters, using match() method and passing regular expression.
for (let x of myString) {
x == x.toLowerCase() ? x = x.toUpperCase() : x = x.toLowerCase();
newString += x; // add on each conversion to the new string
}
} else {
return 'String is empty, or there are no letters to swap.' // In case parameter contains no letters
}
return newString; // output new string
}
// Test the function.
console.log(swapCase('Work Today Was Fun')); // Output: wORK tODAY wAS fUN
console.log(swapCase('87837874---ABCxyz')); // Output: 87837874---abcXYZ
console.log(swapCase('')); // Output: String is empty, or there are no letters to swap.
console.log(swapCase('12345')); // Output: String is empty, or there are no letters to swap.
// This one will fail. But, you can wrap it with if(typeof myString != 'number') to prevent match() method from running and prevent errors.
// console.log(swapCase(12345));
This is a solution that uses regular expressions. It matches each word-char globally, and then performs a function on that matched group.
function swapCase(letters) {
return letters.replace( /\w/g, function(c) {
if (c === c.toLowerCase()) {
return c.toUpperCase();
} else {
return c.toLowerCase();
}
});
}
#this is a program to convert uppercase to lowercase and vise versa and returns the string.
function main(input) {
var i=0;
var string ='';
var arr= [];
while(i<input.length){
string = input.charAt(i);
if(string == string.toUpperCase()){
string = string.toLowerCase();
arr += string;
}else {
string = string.toUpperCase();
arr += string;
}
i++;
}
console.log(arr);
}
Split the string and use the map function to swap the case of letters.
We'll get the array from #1.
Join the array using join function.
`
let str = 'The Quick Brown Fox Jump Over A Crazy Dog'
let swapedStrArray = str.split('').map(a => {
return a === a.toUpperCase() ? a.toLowerCase() : a.toUpperCase()
})
//join the swapedStrArray
swapedStrArray.join('')
console.log('swapedStrArray', swapedStrArray.join(''))
`
A new solution using map
let swappingCases = "So, today we have REALLY good day";
let swapping = swappingCases.split("").map(function(ele){
return ele === ele.toUpperCase()? ele.toLowerCase() : ele.toUpperCase();
}).join("");
console.log(swapping);
As a side note in addition to what has already been said, your original code could work with just some minor modifications: convert the string to an array of 1-character substrings (using split), process this array and convert it back to a string when you're done (using join).
NB: the idea here is to highlight the difference between accessing a character in a string (which can't be modified) and processing an array of substrings (which can be modified). Performance-wise, Fabricator's solution is probably better.
var swapCase = function(str){
var letters = str.split("");
for(var i = 0; i<letters.length; i++){
if(letters[i] === letters[i].toLowerCase()){
letters[i] = letters[i].toUpperCase();
}else {
letters[i] = letters[i].toLowerCase();
}
}
str = letters.join("");
console.log(str);
}
var text = 'So, today we have REALLY good day';
swapCase(text);

More efficient palindrome code

This is a code I used for the coderbyte challenge "Palindrome". The challenge is to return true if str is the same foward and backward(a palindrome). I got all possible points but I know my code is a little ugly. What would be a more efficient way to write this code. It looks like I am repeating myself and it seems like something that could maybe be written with a for loop.I also see how it could return true when its really false if there was a longer palindrome without the use of a for loop:
function Palindrome(str) {
var low=str.toLowerCase()
var first = low.charAt(0);
var last = low.charAt(low.length-1);
var mid = low.charAt(1);
var mid1 = low.charAt(low.length-2);
if(first===last)
if(mid===mid1)
{
return true
}
else
{
return false
}
else
{
return false
}
}
print(Palindrome(readline()));
To check the string if it's a palindrome you just should compare it to its reversed version.
Say the word hello is not a palndrome because its reversed version olleh is not equal to it. But the word eye is a palindrome same as word abba because they're equal to their reversed versions.
Code example:
(function() {
var reverseStr,
isPalindrome,
testStrings;
reverseStr = function(str) {
var chars = [];
for(var i = str.length - 1; i > -1; i--) {
chars.push(str[i]);
}
return chars.join('');
};
isPalindrome = function(str, ignoreCase) {
if(ignoreCase) {
str = str.toLowerCase();
}
return str === reverseStr(str);
};
testStrings = ['abba', 'hello', 'eye'];
for(var i = 0, l = testStrings.length; i < l; i++) {
var word = testStrings[i];
console.log('Word "%s" is %sa palindrome',
word,
isPalindrome(word) ? '' : 'not ');
}
})();
DEMO #1
Another way that could work faster is listed below. Here you don't receive a reversed string to compare but walking towards the middle of the string from its start and its end.
var isPalindrome = function(str, ignoreCase) {
var length,
last,
halfLength,
i;
if(ignoreCase) {
str = str.toLowerCase();
}
length = str.length;
last = length - 1;
halfLength = Math.ceil(length / 2);
for(i = 0; i < halfLength; i++) {
if(str[i] !== str[last - i]) {
return false;
}
}
return true;
};
DEMO #2
function Palindrome(str) {
str = str.toLowerCase();
str = str.split(" ").join("");
return str == str.split("").reverse().join("");
}
This is what I ended up with. Made sure the string was all lowercase so it wouldn't read a potentially true parameter as false, got rid of the spaces, and then returned true/false based off whether or not the string was equal to it's reverse.
Here is an even easier way:
var isPalindrome = function(string) {
string = string.toLowerCase();
if(string.length===0){
return false;
}
for (var i = 0; i < Math.ceil(string.length/2); i++) {
var j = string.length-1-i;
var character1 = string.charAt(i);
var character2 = string.charAt(j);
if (character1 !== character2) {
return false;
}
}
return true;
};
I came across this palindrome coding challenge with a twist, you have to replace all the non-alphanumeric characters(punctuation, spaces and symbols) and of course change the string into lowercase. This is my solution.
function palindrome(str) {
var low = str.toLowerCase();
var filteredStr = low.replace(/[^0-9a-z]/gi, "");
var split = filteredStr.split("");
var backward = split.reverse();
var join = backward.join("");
if (filteredStr === join) {
return true;
} else {
return false;
}
}
if you care about number of lines of code, here's smaller one
function palindrome(str) {
var low = str.toLowerCase();
var filteredStr = low.replace(/[^0-9a-z]/gi, "");
var backward = filteredStr.split("").reverse().join("");
if (filteredStr === backward) {
return true;
} else {
return false;
}
}
the code is beginner friendly and self explanatory, but if you have any questions regarding the code, feel free to ask ;)

Javascript and regex: split string and keep the separator

I have a string:
var string = "aaaaaa<br />† bbbb<br />‡ cccc"
And I would like to split this string with the delimiter <br /> followed by a special character.
To do that, I am using this:
string.split(/<br \/>&#?[a-zA-Z0-9]+;/g);
I am getting what I need, except that I am losing the delimiter.
Here is the example: http://jsfiddle.net/JwrZ6/1/
How can I keep the delimiter?
I was having similar but slight different problem. Anyway, here are examples of three different scenarios for where to keep the deliminator.
"1、2、3".split("、") == ["1", "2", "3"]
"1、2、3".split(/(、)/g) == ["1", "、", "2", "、", "3"]
"1、2、3".split(/(?=、)/g) == ["1", "、2", "、3"]
"1、2、3".split(/(?!、)/g) == ["1、", "2、", "3"]
"1、2、3".split(/(.*?、)/g) == ["", "1、", "", "2、", "3"]
Warning: The fourth will only work to split single characters. ConnorsFan presents an alternative:
// Split a path, but keep the slashes that follow directories
var str = 'Animation/rawr/javascript.js';
var tokens = str.match(/[^\/]+\/?|\//g);
Use (positive) lookahead so that the regular expression asserts that the special character exists, but does not actually match it:
string.split(/<br \/>(?=&#?[a-zA-Z0-9]+;)/g);
See it in action:
var string = "aaaaaa<br />† bbbb<br />‡ cccc";
console.log(string.split(/<br \/>(?=&#?[a-zA-Z0-9]+;)/g));
If you wrap the delimiter in parantheses it will be part of the returned array.
string.split(/(<br \/>&#?[a-zA-Z0-9]+);/g);
// returns ["aaaaaa", "<br />†", "bbbb", "<br />‡", "cccc"]
Depending on which part you want to keep change which subgroup you match
string.split(/(<br \/>)&#?[a-zA-Z0-9]+;/g);
// returns ["aaaaaa", "<br />", "bbbb", "<br />", "cccc"]
You could improve the expression by ignoring the case of letters
string.split(/()&#?[a-z0-9]+;/gi);
And you can match for predefined groups like this: \d equals [0-9] and \w equals [a-zA-Z0-9_]. This means your expression could look like this.
string.split(/<br \/>(&#?[a-z\d]+;)/gi);
There is a good Regular Expression Reference on JavaScriptKit.
If you group the split pattern, its match will be kept in the output and it is by design:
If separator is a regular expression with capturing parentheses, then
each time separator matches, the results (including any undefined
results) of the capturing parentheses are spliced into the output
array.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split#description
You don't need a lookahead or global flag unless your search pattern uses one.
const str = `How much wood would a woodchuck chuck, if a woodchuck could chuck wood?`
const result = str.split(/(\s+)/);
console.log(result);
// We can verify the result
const isSame = result.join('') === str;
console.log({ isSame });
You can use multiple groups. You can be as creative as you like and what remains outside the groups will be removed:
const str = `How much wood would a woodchuck chuck, if a woodchuck could chuck wood?`
const result = str.split(/(\s+)(\w{1,2})\w+/);
console.log(result, result.join(''));
answered it here also JavaScript Split Regular Expression keep the delimiter
use the (?=pattern) lookahead pattern in the regex
example
var string = '500x500-11*90~1+1';
string = string.replace(/(?=[$-/:-?{-~!"^_`\[\]])/gi, ",");
string = string.split(",");
this will give you the following result.
[ '500x500', '-11', '*90', '~1', '+1' ]
Can also be directly split
string = string.split(/(?=[$-/:-?{-~!"^_`\[\]])/gi);
giving the same result
[ '500x500', '-11', '*90', '~1', '+1' ]
I made a modification to jichi's answer, and put it in a function which also supports multiple letters.
String.prototype.splitAndKeep = function(separator, method='seperate'){
var str = this;
if(method == 'seperate'){
str = str.split(new RegExp(`(${separator})`, 'g'));
}else if(method == 'infront'){
str = str.split(new RegExp(`(?=${separator})`, 'g'));
}else if(method == 'behind'){
str = str.split(new RegExp(`(.*?${separator})`, 'g'));
str = str.filter(function(el){return el !== "";});
}
return str;
};
jichi's answers 3rd method would not work in this function, so I took the 4th method, and removed the empty spaces to get the same result.
edit:
second method which excepts an array to split char1 or char2
String.prototype.splitAndKeep = function(separator, method='seperate'){
var str = this;
function splitAndKeep(str, separator, method='seperate'){
if(method == 'seperate'){
str = str.split(new RegExp(`(${separator})`, 'g'));
}else if(method == 'infront'){
str = str.split(new RegExp(`(?=${separator})`, 'g'));
}else if(method == 'behind'){
str = str.split(new RegExp(`(.*?${separator})`, 'g'));
str = str.filter(function(el){return el !== "";});
}
return str;
}
if(Array.isArray(separator)){
var parts = splitAndKeep(str, separator[0], method);
for(var i = 1; i < separator.length; i++){
var partsTemp = parts;
parts = [];
for(var p = 0; p < partsTemp.length; p++){
parts = parts.concat(splitAndKeep(partsTemp[p], separator[i], method));
}
}
return parts;
}else{
return splitAndKeep(str, separator, method);
}
};
usage:
str = "first1-second2-third3-last";
str.splitAndKeep(["1", "2", "3"]) == ["first", "1", "-second", "2", "-third", "3", "-last"];
str.splitAndKeep("-") == ["first1", "-", "second2", "-", "third3", "-", "last"];
An extension function splits string with substring or RegEx and the delimiter is putted according to second parameter ahead or behind.
String.prototype.splitKeep = function (splitter, ahead) {
var self = this;
var result = [];
if (splitter != '') {
var matches = [];
// Getting mached value and its index
var replaceName = splitter instanceof RegExp ? "replace" : "replaceAll";
var r = self[replaceName](splitter, function (m, i, e) {
matches.push({ value: m, index: i });
return getSubst(m);
});
// Finds split substrings
var lastIndex = 0;
for (var i = 0; i < matches.length; i++) {
var m = matches[i];
var nextIndex = ahead == true ? m.index : m.index + m.value.length;
if (nextIndex != lastIndex) {
var part = self.substring(lastIndex, nextIndex);
result.push(part);
lastIndex = nextIndex;
}
};
if (lastIndex < self.length) {
var part = self.substring(lastIndex, self.length);
result.push(part);
};
// Substitution of matched string
function getSubst(value) {
var substChar = value[0] == '0' ? '1' : '0';
var subst = '';
for (var i = 0; i < value.length; i++) {
subst += substChar;
}
return subst;
};
}
else {
result.add(self);
};
return result;
};
The test:
test('splitKeep', function () {
// String
deepEqual("1231451".splitKeep('1'), ["1", "231", "451"]);
deepEqual("123145".splitKeep('1', true), ["123", "145"]);
deepEqual("1231451".splitKeep('1', true), ["123", "145", "1"]);
deepEqual("hello man how are you!".splitKeep(' '), ["hello ", "man ", "how ", "are ", "you!"]);
deepEqual("hello man how are you!".splitKeep(' ', true), ["hello", " man", " how", " are", " you!"]);
// Regex
deepEqual("mhellommhellommmhello".splitKeep(/m+/g), ["m", "hellomm", "hellommm", "hello"]);
deepEqual("mhellommhellommmhello".splitKeep(/m+/g, true), ["mhello", "mmhello", "mmmhello"]);
});
I've been using this:
String.prototype.splitBy = function (delimiter) {
var
delimiterPATTERN = '(' + delimiter + ')',
delimiterRE = new RegExp(delimiterPATTERN, 'g');
return this.split(delimiterRE).reduce((chunks, item) => {
if (item.match(delimiterRE)){
chunks.push(item)
} else {
chunks[chunks.length - 1] += item
};
return chunks
}, [])
}
Except that you shouldn't mess with String.prototype, so here's a function version:
var splitBy = function (text, delimiter) {
var
delimiterPATTERN = '(' + delimiter + ')',
delimiterRE = new RegExp(delimiterPATTERN, 'g');
return text.split(delimiterRE).reduce(function(chunks, item){
if (item.match(delimiterRE)){
chunks.push(item)
} else {
chunks[chunks.length - 1] += item
};
return chunks
}, [])
}
So you could do:
var haystack = "aaaaaa<br />† bbbb<br />‡ cccc"
var needle = '<br \/>&#?[a-zA-Z0-9]+;';
var result = splitBy(haystack , needle)
console.log( JSON.stringify( result, null, 2) )
And you'll end up with:
[
"<br />† bbbb",
"<br />‡ cccc"
]
Most of the existing answers predate the introduction of lookbehind assertions in JavaScript in 2018. You didn't specify how you wanted the delimiters to be included in the result. One typical use case would be sentences delimited by punctuation ([.?!]), where one would want the delimiters to be included at the ends of the resulting strings. This corresponds to the fourth case in the accepted answer, but as noted there, that solution only works for single characters. Arbitrary strings with the delimiters appended at the end can be formed with a lookbehind assertion:
'It is. Is it? It is!'.split(/(?<=[.?!])/)
/* [ 'It is.', ' Is it?', ' It is!' ] */
I know that this is a bit late but you could also use lookarounds
var string = "aaaaaa<br />† bbbb<br />‡ cccc";
var array = string.split(/(?<=<br \/>)/);
console.log(array);
I've also came up with this solution. No regex needed, very readable.
const str = "hello world what a great day today balbla"
const separatorIndex = str.indexOf("great")
const parsedString = str.slice(separatorIndex)
console.log(parsedString)

Categories

Resources