Replace text but not if contain specific characters? - javascript

In JavaScript, I am using the below code to replace text that matches a certain string. The replacement wraps the string like this: "A(hello)". It works great but if there are two strings that are the same, for example: "Hello hi Hello", only the first one will get marked and if I am trying twice, it will get marked double, like this: "A(A(Hello)) Hi Hello".
A solution to this could be to not replace a word if it contains "A(" or is between "A(" and ")"; both would work.
Any idea how it can be achieved?
Note: I cant use replaceAll because if there is already a word that is replaced and a new word is added, then the first one will be overwritten. Therefore I need a solution like above. For example,If I have a string saying "Hello hi", and I mark Hello, it will say "A(Hello) hi", but if I then add Hello again to the text and replace it, it will look like this: A(A(Hello)) hi A(Hello).
Here is what I got so far:
let text = "Hello hi Hello!"
let selection = "Hello"
let A = `A(${selection})`
let addWoman = text.replace(selection, A)

You can use a negative lookahead assertion in your pattern that fails the match if we A( before full word Hello:
(?<!A\()\bHello\b
And replace it with A($&)
RegEx Demo
Code:
let text = "Hello hi Hello!";
let selection = "Hello";
let A = `A(${selection})`;
let re = new RegExp(`(?<!A\\()\\b${selection}\\b`, "g");
let addWoman = text.replace(re, A);
console.log(addWoman);
console.log(addWoman.replace(re, A));

A solution to this could be to not replace a word if it contains "A(" or is between "A(" and ")"; both would work.
To avoid re-matching selection inside a A(...) string, you can match A(...) and capture it into a group so as to know if the group matched, it should be kept, else, match the word of your choice:
let text = "Hello hi Hello!"
let selection = "Hello"
let A = `A(${selection})`
const rx = new RegExp(String.raw`(A\([^()]*\))|${selection.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')}`, 'g')
let addWoman = text.replace(rx, (x,y) => y || A)
console.log(addWoman);
// Replacing the second time does not modify the string:
console.log(addWoman.replace(rx, (x,y) => y || A))
The regex will look like /(A\([^()]*\))|Hello/g, it matches
(A\([^()]*\)) - Group 1: A and then ( followed with zero or more chars other than ( and ) and then a ) char
| - or
Hello - a Hello string.

Related

Replacing url by a value taking from the url with another url

I have a markdown text file with links like that:
[Text](https://docs.google.com/document/d/unique-doc-id-here/edit)
or
[Text2](https://docs.google.com/document/d/unique-doc-id-here")
I want to replace the whole href with another one by taking the unique-doc-id-here, passing that to a function that will return a new href, so in result my urls would look something like that:
[Text](https://new-url-here.com/fragment-unique-id)
or
[Text2](https://new-url-here.com/fragment-unique-id)
I think my problem is to select the unique-doc-id-here, I think I have to use the regex for that.
So the solution could be looking like this:
text.replace(/https:\/\/docs.google.com\/document\/d\/(.*?)*/gm, (x) =>
this.getNewHref(x)
);
However it seems that the regex does not looks quite right, because it does not much all the cases. Any ideas how to fix?
Here is an input text example:
# Title
Text text text.
Text 1 text 1 text 1, abc.
More text
Bullet points
- [abc]
- [bla]
- [cba]
## Title 2
More text:
- A
- B
- C
- D
Text text text text [url1](https://docs.google.com/document/d/2x2my-DRqfSidOsdve4m9bF_eEOJ7RqIWP7tk7PM4qEr) text.
**BOLD.**
## Title
Text2 text1 text3 text
[url2](https://docs.google.com/document/d/4x2mrhsqfGSidOsdve4m9bb_wEOJ7RqsWP7tk7PMPqEb/edit#bookmark=id.mbnek2bdkj8c) text.
More text here
[bla](https://docs.google.com/document/d/6an7_b4Mb0OdxNZdfD3KedfvFtdf2OeGzG40ztfDhi5o9uU/edit)
I've try this regex \w+:\/\/.*?(?=\s) but it does select the last ) symbol
I've applied a proposed solution by #The fourth bird:
function getNewHref(id: string) {
const data = getText();
const element = data.find((x: any) => x.id === id);
if(element?.url) {
return element.url;
} else {
return 'unknown-url'
}
}
data = data.replace(
/\[[^\][]*]\(https?:\/\/docs\.google\.com\/document\/d\/([^\s\\\/)]+)[^\s)]*\)/gm,
(x, g1) => getNewHref(g1)
);
The problem is that the replace function replace the whole thing so what was [...](...) becomes ./new-url or unknown-url but needs to me [original text](new result)
You can make the pattern more specific, and then use the group 1 value.
(\[[^\][]*]\()https?:\/\/docs\.google\.com\/document\/d\/([^\s\\\/)]+)[^\s)]*\)
The pattern in parts matches:
(\[[^\][]*]\() Capture group 1, match from [...]( using a negated character class
https?:\/\/docs\.google\.com\/document\/d\/ Match the leading part of the url
( Capture group 2
[^\s\\\/)]+ Match 1+ chars other than a whitespace char, \ or /
) Close group 1
[^\s)]* Match optional chars other than a whitespace char or )
\) Match )
Regex demo
For example, a happy case scenario where all the keys to be replaced exist (note that you can omit the /m flag as there are no anchors in the pattern)
const text = "[Text](https://docs.google.com/document/d/unique-doc-id-here/edit)";
const regex = /(\[[^\][]*]\()https?:\/\/docs\.google\.com\/document\/d\/([^\s\\\/)]+)[^\s)]*\)/g;
function getNewHref(id) {
const replacements = {
"unique-doc-id-here": `https://docs.google.com/document/d/${id}`
}
return replacements[id];
}
const replacedText = text.replace(regex, (x, g1, g2) => g1 + getNewHref(g2)) + ")";
console.log(replacedText);
You can achieve this by getting the href link from a string by using RegEx and then by splitting that up using forward slash.
Try this (Descriptive comments has been added in the below code snippet) :
const text = 'Text';
// Get the href link using regex
const link = text.match(/"([^"]*)"/)[1];
// Split the string and get the array of link based on the forward slash.
const linkArr = link.split('/')
// get the unique ID from an array.
const uniqueID = linkArr[linkArr.indexOf('d') + 1]
console.log(uniqueID);

Remove a substring to make given word in javascript

I want to know about the algorithm for below question in JavaScript.
Check whether the given word can be "programming" or not by removing the substring between them. You can only remove one substring from the given the word.
Give answer in 'yes' and 'no'
example answer explanation
"progabcramming" yes remove substring 'abc'
"programmmeding" yes remove substring 'med'
"proasdgrammiedg" no u have to remove 2 subtring 'asd' and 'ied'
which is not allowed
"pxrogramming" yes remove substring 'x'
"pxrogramminyg" no u have to remove 2 subtring 'x' and 'y'
which is not allowed
Please tell me an algorithm to solve it
{
// will create a regexp for fuzzy search
const factory = (str) => new RegExp(str.split('').join('(.*?)'), 'i')
const re = factory('test') // re = /t(.*?)e(.*?)s(.*?)t/i
const matches = re.exec('te-abc-st') ?? [] // an array of captured groups
const result = matches
.slice(1) // first element is a full match, we don't need it
.filter(group => group.length) // we're also not interested in empty matches
// now result contains a list of captured groups
// in this particular example a single '-abc-'
}
I'm not sure how efficient this code is, but only thing i can come up with is using regular expression.
const word = 'programming';
const test = ['progabcramming', 'programmmeding', 'proasdgrammiedg', 'pxrogramming', 'pxrogramminyg', 'programming'];
// create regular expression manually
// const regexp = /^(p).+(rogramming)|(pr).+(ogramming)|(pro).+(gramming)|(prog).+(ramming)|(progr).+(amming)|(progra).+(mming)|(program).+(ming)|(programm).+(ing)|(programmi).+(ng)|(programmin).+(g)$/;
// create regular expression programmatically
let text = '/^';
word.split('').forEach((character, i) => {
text += i ? `(${word.substring(0, i)}).+(${word.substring(i)})|` : '';
});
text = text.substring(text.length - 1, 1) + '$/';
const regexp = new RegExp(text);
// content output
let content = '';
test.forEach(element => {
content += `${element}: ${regexp.test(element)}\n`;
});
document.body.innerText = content;

Replace substring with its exact length of another character

I am trying to replace a substring within a string with an exact number of other characters.
Example:
Input: Hello There, General Kenobie!
Output: xxxxx There, xxxxxxx Kenobie!
I can get this to work if I replace it with a preset string:
const text = "'Hello' There, 'General' Kenobie!"
const pattern = /(?:'([^']*)')|(?:"([^"]*)")/g;
console.log(text.replace(pattern, "xxx"));
Output: xxx There, xxx Kenobie!
What am I missing wrapping my head around.
Thanks!
You are using a hard-coded string of 'xxx' as your replacement string. So, that's what you are seeing... the string(s) replaced with 'xxx'.
The .replace() method actually supports a function as the replacement, instead of a string, so that's what you need here.
Docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_function_as_a_parameter
const text = "'Hello' There, 'General' Kenobie!"
const pattern = /(?:'([^']*)')|(?:"([^"]*)")/g;
const newText = text.replace(pattern, (str, m) => 'x'.repeat(m.length));
console.log(newText);
You can always loop through the matches and replace each separately.
let text = "'Hello' There, 'General' Kenobie!"
const pattern = /(?:'([^']*)')|(?:"([^"]*)")/g;
let array1;
while ((array1 = pattern.exec(text)) !== null) {
wrap = array1[0][0];
text = text.replace(array1[0],wrap + "x".repeat(array1[0].length-2) + wrap);
}
console.log(text)

Extending a regex to capture multiple conditions

Currently have the following regex to capture all content within square brackets:
regex = /[^[\]]+(?=])/g
Meaning that:
string = "[Foo: Bar] [Biz: Baz]"
string.match(regex)
In JavaScript will return: ["Foo: Bar", "Biz: Baz"]
for a next step, I want to only get the text that follows a the colon. It is safe to assume that on all matches, we'll consistently have a return where each string in the return array matches the above pattern.
I'm sure there's some way to extend my regex to do this at the same time as finding the text within square brackets, but I'm just not sure how to do so. I've tried using some positive look-aheads, but I have no idea where to add them.
Another simple way:
const regex = /\[(\w+)\s*:\s*(\w+)\]/g;
const string = "[Foo: Bar] [Biz: Baz]";
let match;
while(match = regex.exec(string)){
console.log(`Pro: ${match[1]}`)
console.log(`Val: ${match[2]}`)
}
You can add :) or (: ) if you need also to match the space after the colon):
var string = "[Foo: Bar] [Biz: Baz]"
var regex = /[^[\]:]+(?=])/g;
console.log(string.match(regex));
You can try something like this
\[([^:]+:\s*)([^\]]+)
let regex = /\[([^:]+:\s*)([^\]]+)\]/g
let arr = []
let string = "[Foo: Bar] [Biz: Baz]"
while((arr =regex.exec(string))!== null){
console.log(`key -> ${arr[1]}`)
console.log(`val -> ${arr[2]}`)
}

Get all words starting with X and ending with Y

I have got a textarea with keyup=validate()
I need a javascript function that gets all words starting with # and ending with a character that is not A-Za-z0-9
For example:
This is a text #user1 this is more text #user2. And this is even more #user3!
The function gives an array:
Array("#user1","#user2","#user3");
I am sure there must be a way to do this written on somewhere on the internet if I just google something but I have no idea what I have to look for.. I am very new with regular expresions.
Thank you very much!
The regular expression you want is:
/#[a-z\d]+/ig
This matches # followed by a sequence of letters and numbers. The i modifier makes it case-insensitive, so you don't have to put A-Z in the character class, and g makes it find all the matches.
var str = "This is a text #user1 this is more text #user2. And this is even more #user3!";
var matches = str.match(/#[a-z\d]+/ig);
console.log(matches);
JS
var str = "This is a text #user1 this is more text #user2. And this is even more #user3!",
var textArr = str.split(" ");
for(var i = 0; i < textArr.length; i++) {
var test = textArr[i];
matches = test.match(/^#.*.[A-Za-z0-9]$/);
console.log(matches);
};
Explanation:
You should also read about the regex(http://www.w3schools.com/jsref/jsref_obj_regexp.asp) and match(http://www.w3schools.com/jsref/jsref_match.asp) to get an idea how it works.
Basically, applying ^# means starting the regex look for #. $ means ending with. and .* any character in between.
To Test: http://www.regular-expressions.info/javascriptexample.html
Thanks for the replies above, they've helped me - Where I've written this method that hopefully answers the question about having a start and end regex check.
In this example it looks for ##_ at the start and _## at the end
e.g. ##_ anyTokenYouNeedToFind _##.
Code:
const tokenSearchHelper = (inputText) => {
let matches = inputText.match(/##_[a-zA-Z0-9_\d]+_##/ig);
return matches;
}
const out = tokenSearchHelper("Hello ##_World_##");
console.log(out);

Categories