Replacing letters in specific position in string w/regex(javascript) - javascript

I have this regex (regex101).
\b([\w])(\w*?)\1
Substitution: $1$2#
What I need to do is in JavaScript I iterate thru the chosen and guessed words and am currently checking each position for matches. I have a variable i that holds the current position that I would like to substitute the character # for if both letters in the current position match.
Example, If the current words are mummy and the guessed word is mommy after first iteration we would #ummy & #ommy. After checking 3rd position would have #u#my & #o#my. and etc till end would have #u##y & #o##y.
And can I substitute variables into my regex to accommodate different word pairs. So on another iteration if words were apple and maple code would transform two strings to ap#ple & ma#ple. My JavaScript looks like :
const regex = /\b([\w])(\w*?)\1/gm;
const subst = `$1$2#`;
wirdleString = wirdleString.replace(regex, subst);
guessString = guessString.replace(regex, subst);
I'm sorry my regex skills are very weak. Thanks in advance....

First of all: I don't think this is a task for regexps. The problem is much easier solved using iteration.
That said, as an academic exercise, here's one go to match all characters that differ from "mummy":
(?<=.{0})m(?=.{4})|
(?<=.{1})u(?=.{3})|
(?<=.{2})m(?=.{2})|
(?<=.{3})m(?=.{1})|
(?<=.{3})y(?=.{0})
You can create this by a mapping like this:
const string = "mummy";
const length = string.length;
const exp = string.split('').map((char, index) =>
`(?<=.{${index}})${char}(?=.{${length - index -1}})`
).join('|');
const attempt = "momma";
const result = attempt.replace(new RegExp(exp, 'g'), "#");
// '#o##a'
On a general note, I think an iterative approach would be easier and definitely faster:
const result = [...string].map((char, index) =>
attempt[index] == char ? '#' : char
).join('');

Related

Regex to get the first element of each line

I'm trying to get the first element of each line, be it either a number or a string but when the line starts with a number, my current attempt still includes it:
const totalWords = "===========\n\n 1-test\n\n 2-ests \n\n 1 zfzrf";
const firstWord = totalWords.replace(/\s.*/,'')
The output I get :
1-test
2-ests
1 zfzrf
The output I would like:
1
2
1
Alternatively, if you are interested in a non-regexp version (should be faster)
var str = "===========\n\n 1-test\n\n 2-ests \n\n 1 zfzrf";
var res = str.split("\n");
for (row of res) {
let words = row.trim().split(' ');
let firstWord = words[0].trim();
// get first character, parse to int, validate it is infact integer
let element = firstWord.charAt(0);
if (Number.isInteger(parseInt(element))) {
console.log('Row', row);
console.log('Element: ', element);
}
}
Your Regex should skip leading spaces and then capture everything until a space or a dash, so you might want to go with ^\s*([^ -]+).
(See https://regex101.com/r/u7ELiw/1 for application to your examples)
If you additionally know exactly that you are looking for a single digit, you can instead go for ^\s*(\d)
(See https://regex101.com/r/IWhEQ1/1 again for applications)
Maybe im not too sure what you are asking but why are you using something as convoluted as regex when
line.charAt(0)
works pretty well?

Test for specific number of words to match, with a known separator with Regex in Js

i'm trying to check wether a string matches a set of values and they are seperated by a ; It needs to have ; as a separator.
I go this new RegExp(/\b(Segunda|Terça|Quarta|Quinta|Sexta|Sábado|Domingo)\b/, 'gi').test(str)
If i pass:
'Segunda;Terça', true.
'Segundaaa', false.
'Segunda;Terçaa', true.. Why is it true? how can i avoid this?
Thanks in advance.
[EDIT] code:
const WEEK_DAYS_GROUP_REGEX = /\b(Segunda|Terça|Quarta|Quinta|Sexta|Sábado|Domingo)\b/;
const res = new RegExp(WEEK_DAYS_GROUP_REGEX, 'i').test('Segunda;Terçaa');
console.log(res) // gives true
The /\b(Segunda|Terça|Quarta|Quinta|Sexta|Sábado|Domingo)\b/ pattern with gi modifiers matches any of the alternatives as a whole word, it does not guarantee that the whole string consists of these values only, let alone the ; delimiter.
You may use
^(<ALTERNATIONS>)(?:;(<ALTERNATIONS>))*$
See the pattern demo.
In JS, you do not need to use that long pattern, you may build the pattern dynamically:
const strs = ["Segunda;Terça", "Segundaaa", "Segunda;Terçaa"];
const vals = "Segunda|Terça|Quarta|Quinta|Sexta|Sábado|Domingo";
let rx = new RegExp("^(?:" + vals + ")(?:;(?:" + vals + "))*$", "i");
console.log(rx);
for (let s of strs) {
console.log(s,"=>",rx.test(s));
}
Note that the non-capturing groups (?:...) are preferred when there is no need extracting submatches, group values.

Substring of a Turkish String

I have a string like this
var element = "İstanbul";
and when I convert it to lower case like this:
var element = element.toLowerCase();
it becomes
"istanbul"
I need the substring of the lower case string "istanbul".
So, when I do this before the lowerCase operation
element.substr(0,2)
the output is correct
but when I do the following it's wrong from which I know substr(0,2) should give "is" instead of i
Why is it happening and how can I correct this?
It is happening because during changing to lower case the string is normalised, and the İ turns into 2 characters: "i" ( http://www.fileformat.info/info/unicode/char/0069/index.htm) and "̇" (the latter is a diacritical mark http://www.fileformat.info/info/unicode/char/0307/index.htm).
To prevent it you may split the string into characters using the ES2015 string iteration facilities and lower case the characters separately:
const arr_l_new = [...str].map(s => s.toLowerCase());
Then you can take the first N characters:
const first_2_chars = arr_l_new.slice(0, 2).join('');
Note: that if you count the length of the first_2_chars you will notice it has the length of 3, due to the diacritic character, which is actually not visible for the lower case i.
var str = "İstanbul";
const arr_l = [...str].map(s => s.toLowerCase());
const first_2_l = arr_l.slice(0, 2).join('');
console.log(first_2_l, first_2_l.length);
try
element.toLowerCase().replace(new RegExp("İ".toLowerCase(), "g"), "i");
instead of
element.toLowerCase();

How to remove the last matched regex pattern in javascript

I have a text which goes like this...
var string = '~a=123~b=234~c=345~b=456'
I need to extract the string such that it splits into
['~a=123~b=234~c=345','']
That is, I need to split the string with /b=.*/ pattern but it should match the last found pattern. How to achieve this using RegEx?
Note: The numbers present after the equal is randomly generated.
Edit:
The above one was just an example. I did not make the question clear I guess.
Generalized String being...
<word1>=<random_alphanumeric_word>~<word2>=<random_alphanumeric_word>..~..~..<word2>=<random_alphanumeric_word>
All have random length and all wordi are alphabets, the whole string length is not fixed. the only text known would be <word2>. Hence I needed RegEx for it and pattern being /<word2>=.*/
This doesn't sound like a job for regexen considering that you want to extract a specific piece. Instead, you can just use lastIndexOf to split the string in two:
var lio = str.lastIndexOf('b=');
var arr = [];
var arr[0] = str.substr(0, lio);
var arr[1] = str.substr(lio);
http://jsfiddle.net/NJn6j/
I don't think I'd personally use a regex for this type of problem, but you can extract the last option pair with a regex like this:
var str = '~a=123~b=234~c=345~b=456';
var matches = str.match(/^(.*)~([^=]+=[^=]+)$/);
// matches[1] = "~a=123~b=234~c=345"
// matches[2] = "b=456"
Demo: http://jsfiddle.net/jfriend00/SGMRC/
Assuming the format is (~, alphanumeric name, =, and numbers) repeated arbitrary number of times. The most important assumption here is that ~ appear once for each name-value pair, and it doesn't appear in the name.
You can remove the last token by a simple replacement:
str.replace(/(.*)~.*/, '$1')
This works by using the greedy property of * to force it to match the last ~ in the input.
This can also be achieved with lastIndexOf, since you only need to know the index of the last ~:
str.substring(0, (str.lastIndexOf('~') + 1 || str.length() + 1) - 1)
(Well, I don't know if the code above is good JS or not... I would rather write in a few lines. The above is just for showing one-liner solution).
A RegExp that will give a result that you may could use is:
string.match(/[a-z]*?=(.*?((?=~)|$))/gi);
// ["a=123", "b=234", "c=345", "b=456"]
But in your case the simplest solution is to split the string before extract the content:
var results = string.split('~'); // ["", "a=123", "b=234", "c=345", "b=456"]
Now will be easy to extract the key and result to add to an object:
var myObj = {};
results.forEach(function (item) {
if(item) {
var r = item.split('=');
if (!myObj[r[0]]) {
myObj[r[0]] = [r[1]];
} else {
myObj[r[0]].push(r[1]);
}
}
});
console.log(myObj);
Object:
a: ["123"]
b: ["234", "456"]
c: ["345"]
(?=.*(~b=[^~]*))\1
will get it done in one match, but if there are duplicate entries it will go to the first. Performance also isn't great and if you string.replace it will destroy all duplicates. It would pass your example, but against '~a=123~b=234~c=345~b=234' it would go to the first 'b=234'.
.*(~b=[^~]*)
will run a lot faster, but it requires another step because the match comes out in a group:
var re = /.*(~b=[^~]*)/.exec(string);
var result = re[1]; //~b=234
var array = string.split(re[1]);
This method will also have the with exact duplicates. Another option is:
var regex = /.*(~b=[^~]*)/g;
var re = regex.exec(string);
var result = re[1];
// if you want an array from either side of the string:
var array = [string.slice(0, regex.lastIndex - re[1].length - 1), string.slice(regex.lastIndex, string.length)];
This actually finds the exact location of the last match and removes it regex.lastIndex - re[1].length - 1 is my guess for the index to remove the ellipsis from the leading side, but I didn't test it so it might be off by 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