JS Regex returning -1 & 0 - javascript

I was tasked with the following:
take a string
print each of the vowels on a new line (in order) then...
print each of the consonants on a new line (in order)
The problem I found was with the regex. I originally used...
/[aeiouAEIOU\s]/g
But this would return 0 with a vowel and -1 with a consonant (so everything happened in reverse).
I really struggled to understand why and couldn't for the life of me find the answer. In the end it was simple enough to just invert the string but I want to know why this is happening the way it is. Can anyone help?
let i;
let vowels = /[^aeiouAEIOU\s]/g;
let array = [];
function vowelsAndConsonants(s) {
for(i=0;i<s.length;i++){
//if char is vowel then push to array
if(s[i].search(vowels)){
array.push(s[i]);
}
}
for(i=0;i<s.length;i++){
//if char is cons then push to array
if(!s[i].search(vowels)){
array.push(s[i]);
}
}
for(i=0;i<s.length;i++){
console.log(array[i]);
}
}
vowelsAndConsonants("javascript");

if(vowels.test(s[i])){ which will return true or false if it matches, or
if(s[i].search(vowels) !== -1){ and if(s[i].search(vowels) === -1){
is what you want if you want to fix your code.
-1 is not falsey so your if statement will not function correctly. -1 is what search returns if it doesn't find a match. It has to do this because search() returns the index position of the match, and the index could be anywhere from 0 to Infinity, so only negative numbers are available to indicate non-existent index:
MDN search() reference
Below is a RegEx that matches vowel OR any letter OR other, effectively separating out vowel, consonant, everything else into 3 capture groups. This makes it so you don't need to test character by character and separate them out manually.
Then iterates and pushes them into their respective arrays with a for-of loop.
const consonants = [], vowels = [], other = [];
const str = ";bat cat set rat. let ut cut mut,";
for(const [,cons,vow,etc] of str.matchAll(/([aeiouAEIOU])|([a-zA-Z])|(.)/g))
cons&&consonants.push(cons) || vow&&vowels.push(vow) || typeof etc === 'string'&&other.push(etc)
console.log(
consonants.join('') + '\n' + vowels.join('') + '\n' + other.join('')
)

There are a couple of inbuilt functions available:
let some_string = 'Mary had a little lamb';
let vowels = [...some_string.match(/[aeiouAEIOU\s]/g)];
let consonents = [...some_string.match(/[^aeiouAEIOU\s]/g)];
console.log(vowels);
console.log(consonents);

I think that you don't understand correctly how your regular expression works. In the brackets you have only defined a set of characters you want to match /[^aeiouAEIOU\s]/g and further by using the caret [^]as first in your group, you say that you want it to match everything but the characters in the carets. Sadly you don't provide an example of input and expected output, so I am only guessing, but I thing you could do the following:
let s = "avndexleops";
let keep_vowels = s.replace(/[^aeiouAEIOU\s]/g, '');
console.log(keep_vowels);
let keep_consonants = s.replace(/[aeiouAEIOU\s]/g, '');
console.log(keep_consonants);
Please provide example of expected input and output.

You used:
/[^aeiouAEIOU\s]/g
Instead of:
/[aeiouAEIOU\s]/g
^ means "not", so your REGEX /[^aeiouAEIOU\s]/g counts all the consonants.

Related

Remove all consonants in a string before a vowel then add a character

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'));

changing lower to uppercase without built in functions of string

I have created the loop to search the array for the string length and after that I am sort of stuck to where to go to next after that because y of the others.
function uppp(string) {
var c = '';
This seems like a homework question, so I'm only going to give you hints!
First, look into the concept of ASCII codes:
https://www.w3schools.com/charsets/ref_html_ascii.asp
You'll notice that the character codes for a-z (lowercase) are 97-122.
Likewise, the character codes for A-Z (capitalized) are 65-90.
High-level solution
Iterate through your string, checking if each character is lowercase.
Notes: 'a'.charCodeAt(0) === 97 and String.fromCharCode('a'.charCodeAt(0) + 1); === 'b'
With these notes in mind, see if you can develop a solution.
Look at the array functions such as map(), join(), and indexOf(). How could two arrays, one of lowercase and one of uppercase letters, be combined with these methods to produce the desired outcome?
var s = 'String is COOL!',
result = '',
lower = ['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'],
upper = ['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'];
function makeUpper(x) {
let index = lower.indexOf(x);
return index > -1 ? upper[index] : x;
}
result = [].map.call(s, makeUpper).join('');
console.log(result);

How to check if one element of an array matches another element in same array?

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);

Find indexOf character after certain index

Pretty basic but I'm afraid I'm overlooking a simple solution. I have the following string ... IBAN: NL56INGB06716xxxxx ...
I need the accountnumber so I'm looking for indexOf("IBAN: ") but now I need to find the next space/whitespace char after that index.
I don't really think I would need a loop for this but it's the best I can come up with. Regex capture group maybe better? How would I do that?
From MDN String.prototype.indexOf
str.indexOf(searchValue[, fromIndex])
fromIndex
Optional. The location within the calling string to start the search from. It can be any integer. The default value is 0.
n.b. .indexOf will only look for a specific substring, if you want to find a choice from many characters, you will either need to loop and compare or use RegExp
Gracious example
var haystack = 'foo_ _IBAN: Bar _ _';
var needle = 'IBAN: ',
i = haystack.indexOf(needle),
j;
if (i === -1) {
// no match, do something special
console.warn('One cannot simply find a needle in a haystack');
}
j = haystack.indexOf(' ', i + needle.length);
// now we have both matches, we can do something fancy
if (j === -1) {
j = haystack.length; // no match, set to end?
}
haystack.slice(i + needle.length, j); // "Bar"
While you can pass a starting index as Paul suggested, it would seem that a simple regex may just be easier.
var re = /IBAN:\s*(\S+)/
The capture group will hold the sequence of non-whitespace characters after the IBAN:
var match = re.exec(my_str)
if (match) {
console.log(match[1]);
}

split string only on first instance of specified character

In my code I split a string based on _ and grab the second item in the array.
var element = $(this).attr('class');
var field = element.split('_')[1];
Takes good_luck and provides me with luck. Works great!
But, now I have a class that looks like good_luck_buddy. How do I get my javascript to ignore the second _ and give me luck_buddy?
I found this var field = element.split(new char [] {'_'}, 2); in a c# stackoverflow answer but it doesn't work. I tried it over at jsFiddle...
Use capturing parentheses:
'good_luck_buddy'.split(/_(.*)/s)
['good', 'luck_buddy', ''] // ignore the third element
They are defined as
If separator contains capturing parentheses, matched results are returned in the array.
So in this case we want to split at _.* (i.e. split separator being a sub string starting with _) but also let the result contain some part of our separator (i.e. everything after _).
In this example our separator (matching _(.*)) is _luck_buddy and the captured group (within the separator) is lucky_buddy. Without the capturing parenthesis the luck_buddy (matching .*) would've not been included in the result array as it is the case with simple split that separators are not included in the result.
We use the s regex flag to make . match on newline (\n) characters as well, otherwise it would only split to the first newline.
What do you need regular expressions and arrays for?
myString = myString.substring(myString.indexOf('_')+1)
var myString= "hello_there_how_are_you"
myString = myString.substring(myString.indexOf('_')+1)
console.log(myString)
I avoid RegExp at all costs. Here is another thing you can do:
"good_luck_buddy".split('_').slice(1).join('_')
With help of destructuring assignment it can be more readable:
let [first, ...rest] = "good_luck_buddy".split('_')
rest = rest.join('_')
A simple ES6 way to get both the first key and remaining parts in a string would be:
const [key, ...rest] = "good_luck_buddy".split('_')
const value = rest.join('_')
console.log(key, value) // good, luck_buddy
Nowadays String.prototype.split does indeed allow you to limit the number of splits.
str.split([separator[, limit]])
...
limit Optional
A non-negative integer limiting the number of splits. If provided, splits the string at each occurrence of the specified separator, but stops when limit entries have been placed in the array. Any leftover text is not included in the array at all.
The array may contain fewer entries than limit if the end of the string is reached before the limit is reached.
If limit is 0, no splitting is performed.
caveat
It might not work the way you expect. I was hoping it would just ignore the rest of the delimiters, but instead, when it reaches the limit, it splits the remaining string again, omitting the part after the split from the return results.
let str = 'A_B_C_D_E'
const limit_2 = str.split('_', 2)
limit_2
(2) ["A", "B"]
const limit_3 = str.split('_', 3)
limit_3
(3) ["A", "B", "C"]
I was hoping for:
let str = 'A_B_C_D_E'
const limit_2 = str.split('_', 2)
limit_2
(2) ["A", "B_C_D_E"]
const limit_3 = str.split('_', 3)
limit_3
(3) ["A", "B", "C_D_E"]
This solution worked for me
var str = "good_luck_buddy";
var index = str.indexOf('_');
var arr = [str.slice(0, index), str.slice(index + 1)];
//arr[0] = "good"
//arr[1] = "luck_buddy"
OR
var str = "good_luck_buddy";
var index = str.indexOf('_');
var [first, second] = [str.slice(0, index), str.slice(index + 1)];
//first = "good"
//second = "luck_buddy"
You can use the regular expression like:
var arr = element.split(/_(.*)/)
You can use the second parameter which specifies the limit of the split.
i.e:
var field = element.split('_', 1)[1];
Replace the first instance with a unique placeholder then split from there.
"good_luck_buddy".replace(/\_/,'&').split('&')
["good","luck_buddy"]
This is more useful when both sides of the split are needed.
I need the two parts of string, so, regex lookbehind help me with this.
const full_name = 'Maria do Bairro';
const [first_name, last_name] = full_name.split(/(?<=^[^ ]+) /);
console.log(first_name);
console.log(last_name);
Non-regex solution
I ran some benchmarks, and this solution won hugely:1
str.slice(str.indexOf(delim) + delim.length)
// as function
function gobbleStart(str, delim) {
return str.slice(str.indexOf(delim) + delim.length);
}
// as polyfill
String.prototype.gobbleStart = function(delim) {
return this.slice(this.indexOf(delim) + delim.length);
};
Performance comparison with other solutions
The only close contender was the same line of code, except using substr instead of slice.
Other solutions I tried involving split or RegExps took a big performance hit and were about 2 orders of magnitude slower. Using join on the results of split, of course, adds an additional performance penalty.
Why are they slower? Any time a new object or array has to be created, JS has to request a chunk of memory from the OS. This process is very slow.
Here are some general guidelines, in case you are chasing benchmarks:
New dynamic memory allocations for objects {} or arrays [] (like the one that split creates) will cost a lot in performance.
RegExp searches are more complicated and therefore slower than string searches.
If you already have an array, destructuring arrays is about as fast as explicitly indexing them, and looks awesome.
Removing beyond the first instance
Here's a solution that will slice up to and including the nth instance. It's not quite as fast, but on the OP's question, gobble(element, '_', 1) is still >2x faster than a RegExp or split solution and can do more:
/*
`gobble`, given a positive, non-zero `limit`, deletes
characters from the beginning of `haystack` until `needle` has
been encountered and deleted `limit` times or no more instances
of `needle` exist; then it returns what remains. If `limit` is
zero or negative, delete from the beginning only until `-(limit)`
occurrences or less of `needle` remain.
*/
function gobble(haystack, needle, limit = 0) {
let remain = limit;
if (limit <= 0) { // set remain to count of delim - num to leave
let i = 0;
while (i < haystack.length) {
const found = haystack.indexOf(needle, i);
if (found === -1) {
break;
}
remain++;
i = found + needle.length;
}
}
let i = 0;
while (remain > 0) {
const found = haystack.indexOf(needle, i);
if (found === -1) {
break;
}
remain--;
i = found + needle.length;
}
return haystack.slice(i);
}
With the above definition, gobble('path/to/file.txt', '/') would give the name of the file, and gobble('prefix_category_item', '_', 1) would remove the prefix like the first solution in this answer.
Tests were run in Chrome 70.0.3538.110 on macOSX 10.14.
Use the string replace() method with a regex:
var result = "good_luck_buddy".replace(/.*?_/, "");
console.log(result);
This regex matches 0 or more characters before the first _, and the _ itself. The match is then replaced by an empty string.
Javascript's String.split unfortunately has no way of limiting the actual number of splits. It has a second argument that specifies how many of the actual split items are returned, which isn't useful in your case. The solution would be to split the string, shift the first item off, then rejoin the remaining items::
var element = $(this).attr('class');
var parts = element.split('_');
parts.shift(); // removes the first item from the array
var field = parts.join('_');
Here's one RegExp that does the trick.
'good_luck_buddy' . split(/^.*?_/)[1]
First it forces the match to start from the
start with the '^'. Then it matches any number
of characters which are not '_', in other words
all characters before the first '_'.
The '?' means a minimal number of chars
that make the whole pattern match are
matched by the '.*?' because it is followed
by '_', which is then included in the match
as its last character.
Therefore this split() uses such a matching
part as its 'splitter' and removes it from
the results. So it removes everything
up till and including the first '_' and
gives you the rest as the 2nd element of
the result. The first element is "" representing
the part before the matched part. It is
"" because the match starts from the beginning.
There are other RegExps that work as
well like /_(.*)/ given by Chandu
in a previous answer.
The /^.*?_/ has the benefit that you
can understand what it does without
having to know about the special role
capturing groups play with replace().
if you are looking for a more modern way of doing this:
let raw = "good_luck_buddy"
raw.split("_")
.filter((part, index) => index !== 0)
.join("_")
Mark F's solution is awesome but it's not supported by old browsers. Kennebec's solution is awesome and supported by old browsers but doesn't support regex.
So, if you're looking for a solution that splits your string only once, that is supported by old browsers and supports regex, here's my solution:
String.prototype.splitOnce = function(regex)
{
var match = this.match(regex);
if(match)
{
var match_i = this.indexOf(match[0]);
return [this.substring(0, match_i),
this.substring(match_i + match[0].length)];
}
else
{ return [this, ""]; }
}
var str = "something/////another thing///again";
alert(str.splitOnce(/\/+/)[1]);
For beginner like me who are not used to Regular Expression, this workaround solution worked:
var field = "Good_Luck_Buddy";
var newString = field.slice( field.indexOf("_")+1 );
slice() method extracts a part of a string and returns a new string and indexOf() method returns the position of the first found occurrence of a specified value in a string.
This should be quite fast
function splitOnFirst (str, sep) {
const index = str.indexOf(sep);
return index < 0 ? [str] : [str.slice(0, index), str.slice(index + sep.length)];
}
console.log(splitOnFirst('good_luck', '_')[1])
console.log(splitOnFirst('good_luck_buddy', '_')[1])
This worked for me on Chrome + FF:
"foo=bar=beer".split(/^[^=]+=/)[1] // "bar=beer"
"foo==".split(/^[^=]+=/)[1] // "="
"foo=".split(/^[^=]+=/)[1] // ""
"foo".split(/^[^=]+=/)[1] // undefined
If you also need the key try this:
"foo=bar=beer".split(/^([^=]+)=/) // Array [ "", "foo", "bar=beer" ]
"foo==".split(/^([^=]+)=/) // [ "", "foo", "=" ]
"foo=".split(/^([^=]+)=/) // [ "", "foo", "" ]
"foo".split(/^([^=]+)=/) // [ "foo" ]
//[0] = ignored (holds the string when there's no =, empty otherwise)
//[1] = hold the key (if any)
//[2] = hold the value (if any)
a simple es6 one statement solution to get the first key and remaining parts
let raw = 'good_luck_buddy'
raw.split('_')
.reduce((p, c, i) => i === 0 ? [c] : [p[0], [...p.slice(1), c].join('_')], [])
You could also use non-greedy match, it's just a single, simple line:
a = "good_luck_buddy"
const [,g,b] = a.match(/(.*?)_(.*)/)
console.log(g,"and also",b)

Categories

Resources