Javascript using .replace() at a specific index of the search - javascript

Is there a function that can replace a string within a string once at a specific index of the string? Example:
var string1="my text is my text and my big text";
var string2="my";
string1.replaceAt(string2,"your",2);
and the resultant output would be "my text is my text and your big text"

You can do this with a little bit of manipulation, not requiring any regex.
I used this function to fetch the position (index) of another string within a string.
From there, it's as simple as returning a substring from the beginning to the found index, injecting your replacement, and then returning the rest of the string.
function replaceAt(s, subString, replacement, index) {
const p = s.split(subString, index+1).join(subString);
return p.length < s.length ? p + replacement + s.slice(p.length + subString.length) : s;
}
console.log(replaceAt("my text is my text and my big text", "my", "your", 2))
console.log(replaceAt("my text is my text and that's all", "my", "your", 2))
console.log(replaceAt("my text is my my my my text", "my", "your", 2))

There's not a built-in way to do that, but you can exploit the fact that .replace() can be passed a function:
let count = 0;
console.log("my text is my text and my big text".replace(/my/g, function() {
if (count++ === 2) return "your";
return "my";
}));
The function is passed the matched string and any groups as arguments, but in this case that's not really necessary.

Here is a prototype version
String.prototype.replaceAt = function(str1,str2,pos) {
let count = 0;
let re = new RegExp(str1,"g");
return this.replace(re, function() {
return (count++ === pos) ? str2 : str1;
});
}
console.log("my text is my text and my big text".replaceAt("my","your",2))

Beside the use of a function for replacement, you could use the given variable for the position directly for decrementing and take this value as check for replacement or not.
const replace = (string, s, r, p) => string.replace(new RegExp(s, 'g'), m => p-- ? m : r);
var string = 'my text is my text and my big text';
console.log(replace(string, 'my', 'your', 2));

As we need to look for index where we need to replace our word and for other words we don't need any change so used "map". Converted string to array by split and using map returned each element as it is and just replaced word when reached to specific index (by logic of index-- == 0). Finally joined back array to string.
function replace(text, strFrom, strTo, index) {
return text.split(' ')
.map(d => d == strFrom && index-- == 0
? strTo
: d)
.join(' ')
}
var string = 'my text is my text and my big text';
string = replace(string, 'my', 'your', 2)
console.log(string)

Related

What's wrong with the first element in HOF in JS?

Here I have a string with repeated chars,
I want to get the string without repetition, so I used map with specific index, but I don't know what's wrong with the 1st repeated char, when I used slice(i+1,1) all thing was good, but when I used slice(i,1) also all thing was good except the first repeated char.
The output of the 1st code is : elzero
The output of the 2nd code is : eelzero
What's the problem?
Here's the two codes:
let myString = "EElllzzzzzzzeroo";
let elzero = myString
.split("")
.map(function(ele, i = 1, myString) {
console.log(i);
if (myString[i] === myString[i + 1]) {
return myString[i + 1].slice(i + 1, 1);
} else {
return myString[i];
}
})
.join("");
console.log(elzero);
// Elzero
And here's the second:
let myString = "EElllzzzzzzzeroo";
let elzero = myString
.split("")
.map(function(ele, i = 1, myString) {
console.log(i);
if (myString[i] === myString[i + 1]) {
return myString[i].slice(i, 1);
} else {
return myString[i];
}
})
.join("");
console.log(elzero);
// EElzero
Your first code block only works because your .slice() happens to be returning empty strings. You really don't need to use .slice() at all, since all it does is generate an empty string which is then removed when you use .join(). The .slice() call does NOT update/remove an element like .splice() on an array does.
In your second code block, you're doing:
myString[i].slice(i, 1);
If i is 0 then you are on the first character in your string, so myString[0] is "E". Here, .slice(0, 1) says, give me the string that starts at index 0 up to, but not including index 1. This results in "E" being returned.
For all of your subsequent calls myString[i] gives you back one character (so it only has one index, 0), but you're trying to use .slice() to get a portion of your string from indexes that don't exist as i is bigger than your index. Moreover, i is bigger than your second argument, so when i is 1, you're asking to get get the string portion from index 1 (which doesn't exist) up to but not including 1, which results in an empty string. When i is 2, its a similar situation, which results in an empty string.
What you should be doing is removing the use of .slice() altogether, and instead return an empty string when you want to omit the character:
let myString = "EElllzzzzzzzeroo";
let elzero = myString
.split("")
.map(function(ele, i, myString) {
if (ele === myString[i + 1]) { // if current char (ele) equals the next char (myString[i+1]), then "remove" the current char by mapping it to an empty string (this is removed when we `.join("")`)
return "";
} else {
return ele;
}
})
.join("");
console.log(elzero); // Elzero
More concisely, you can rewrite this with arrow functions and a ternary like so. You can also use Array.from() to iterate the code points (ie: characters) within your string, and use the second argument as the mapping function:
const myString = "EElllzzzzzzzeroo";
const elzero = Array.from(
myString,
(ele, i) => ele === myString[i + 1] ? "" : ele
).join("");
console.log(elzero); // Elzero
Alternatively, using a different approach, you can use .replace() with a regular expression to remove repeated characters:
const myString = "EElllzzzzzzzeroo";
const elzero = myString.replace(/(.)\1*/g, '$1')
console.log(elzero); // Elzero
The (.) captures a character and groups it, the \1* is a backreference that matches zero or more of the grouped character repeated, and the "$1" replaces the matched characters with the singular grouped character.
Since you are returning myString[i], not myString[i+1], you have to compare myString[i] with myString[i-1]:
let myString = "EElllzzzzzzzeroo";
let elzero = myString
.split("")
.map(function(ele, i = 1, myString) {
console.log(i);
// if (myString[i] === myString[i + 1]) {
if (myString[i-1] === myString[i]) {
return myString[i].slice(i, 1);
} else {
return myString[i];
}
})
.join("");
console.log(elzero);
// EElzero

Javascript - Replace a character inside a substring of a string

I have a string like "this/ is an example abc/def/fgh/uio to give you an example"
I'd like to target the longest word and replace on this substring any "/" by a "+".
I manage to identify the longest word and I would know how to replace ALL "/" by a "+" BUT I don't know how to replace the "/" only in the longest word.
Here's what I've got so far
//identify longest word in string
function longestWord(str) {
var words = str.split(' ');
return words.reduce(longer);
}
function longer(champ, contender) {
return (contender.length > champ.length) ? contender: champ;
}
//purely given an exemple, some strigns won't be exactly like this
var text2 = "this/ is an example abc/def/fgh/uio to give you an example"
if (longestWord(text2) > 30 ) {
text2.replace(/\//g, ' / ');
}
The problem is this will also replace the "/" on the substring "this/", and I don't want that.
How to achieve this?
Your longestWord function returns the longest word in the string, so you can pass that string alone (not a regular expression) as the first argument to .replace, and replace with (the second argument) the /\//g called on that longest word:
function getLongestWord(str) {
var words = str.split(' ');
return words.reduce(longer);
}
function longer(champ, contender) {
return (contender.length > champ.length) ? contender: champ;
}
var text2 = "this/ is an example abc/def/fgh/uio to give you an example"
const longestWord = getLongestWord(text2);
const output = text2.replace(longestWord, longestWord.replace(/\//g, '+'));
console.log(output);
#CertainPermance's solution is far more elegant (and I think performant) than this, but as I'd written the answer I thought I may as well put it in.
It's fairly similar, in truth, though in this instance we get the index of the word and use that to perform the replace, which at the time of writing I thought was necessary. Now looking at the better solution, I realise such a check is not needed, as the longest word in a string will not feature in any other words, so it's easy and safe to simply perform a replace on it.
const data = "this/ is an example abc/def/fgh/uio to give you an example";
const getLongestWordIndex = stringIn => stringIn
.split(' ')
.reduce(
(prev, curr, i) => curr.length > prev.length ? {
index: i,
length: curr.length
} : prev,
{
length: -1,
index: -1
}
).index
const replaceLongestWord = (sentence, replacer) => {
const longestWordIndex = getLongestWordIndex(sentence);
const words = data.split(' ');
return Object.values({
...words,
[longestWordIndex]: replacer(words[longestWordIndex])
}).join(' ')
}
const wordReplaceFunction = word => word.replace(/\//g, '+')
const result = replaceLongestWord(data, wordReplaceFunction);
console.dir(result)

How to get value in $1 in regex to a variable for further manipulation [duplicate]

You can backreference like this in JavaScript:
var str = "123 $test 123";
str = str.replace(/(\$)([a-z]+)/gi, "$2");
This would (quite silly) replace "$test" with "test". But imagine I'd like to pass the resulting string of $2 into a function, which returns another value. I tried doing this, but instead of getting the string "test", I get "$2". Is there a way to achieve this?
// Instead of getting "$2" passed into somefunc, I want "test"
// (i.e. the result of the regex)
str = str.replace(/(\$)([a-z]+)/gi, somefunc("$2"));
Like this:
str.replace(regex, function(match, $1, $2, offset, original) { return someFunc($2); })
Pass a function as the second argument to replace:
str = str.replace(/(\$)([a-z]+)/gi, myReplace);
function myReplace(str, group1, group2) {
return "+" + group2 + "+";
}
This capability has been around since Javascript 1.3, according to mozilla.org.
Using ESNext, quite a dummy links replacer but just to show-case how it works :
let text = 'Visit http://lovecats.com/new-posts/ and https://lovedogs.com/best-dogs NOW !';
text = text.replace(/(https?:\/\/[^ ]+)/g, (match, link) => {
// remove ending slash if there is one
link = link.replace(/\/?$/, '');
return `${link.substr(link.lastIndexOf('/') +1)}`;
});
document.body.innerHTML = text;
Note: Previous answer was missing some code. It's now fixed + example.
I needed something a bit more flexible for a regex replace to decode the unicode in my incoming JSON data:
var text = "some string with an encoded 's' in it";
text.replace(/&#(\d+);/g, function() {
return String.fromCharCode(arguments[1]);
});
// "some string with an encoded 's' in it"
If you would have a variable amount of backreferences then the argument count (and places) are also variable. The MDN Web Docs describe the follwing syntax for sepcifing a function as replacement argument:
function replacer(match[, p1[, p2[, p...]]], offset, string)
For instance, take these regular expressions:
var searches = [
'test([1-3]){1,3}', // 1 backreference
'([Ss]ome) ([A-z]+) chars', // 2 backreferences
'([Mm][a#]ny) ([Mm][0o]r[3e]) ([Ww][0o]rd[5s])' // 3 backreferences
];
for (var i in searches) {
"Some string chars and many m0re w0rds in this test123".replace(
new RegExp(
searches[i]
function(...args) {
var match = args[0];
var backrefs = args.slice(1, args.length - 2);
// will be: ['Some', 'string'], ['many', 'm0re', 'w0rds'], ['123']
var offset = args[args.length - 2];
var string = args[args.length - 1];
}
)
);
}
You can't use 'arguments' variable here because it's of type Arguments and no of type Array so it doesn't have a slice() method.

javascript pass obj to string replace

i have object with keys and values , i want to pass this object to replace function and replace key with value
function highlight(){
var mapObj = {} //this is the obj to pass to replace
//get the input value
var whatInTextInput = document.querySelector(".toGetText").value;
//trim and split the string into array of words
var whatInTextInput_words_array = whatInTextInput.trim().split(' ');
//build the object with keys and value
for(let item in whatInTextInput_words_array){
mapObj[whatInTextInput_words_array[item]] = "<mark class='highlight'>"+[whatInTextInput_words_array[item]]+"</mark>"
}
// this is just the test string
var str = "I have a cat, a dog, and a goat.";
//here how to pass mapObj instead of /cat|dog|goat/gi ???
str = str.replace(/cat|dog|goat/gi, function(key) {
return mapObj[key];
});
//show the final result
document.querySelector(".inputText").innerHTML = str;
}
.highlight {
background: yellow;
}
<button onclick="highlight()">Highlight</button>
<input type="text" class="toGetText"/>
<div class="inputText">I have a cat, a dog, and a goat.</div>
And if i didn't type the other words i got undefined as a result.
How to pass mapObj obj which has keys and value to replace function, instead of const value /cat|dog|goat/?
You're going to have to get the words from your user input and build a regex from them. .replace simply takes a regex; it does not deal with objects directly.
There are some subtle points here: You'll need to escape any regex meta-characters, and you need to make sure any keys that are prefixes of other keys are listed later (e.g. if you have both cat and caterpillar, the regex needs to be caterpillar|cat, not cat|caterpillar, or else caterpillar will never match).
It's not a good idea to have a mapping object in this case, either: You want to do case insensitive matching, so you can't just pre-compute a replacement string: If you don't normalize your matched strings, you'll just get undefined (because e.g. CaT isn't among the keys), and if you do, you'll destroy the original case of the text (e.g. CaT would turn into <mark class="highlight">cat</mark> (all lowercase)).
The easiest solution is to take all words from the input string, sort them by descending length (to ensure any prefix strings are listed later), escape all regex metacharacters, and join them with |. However, if the input list is empty, that would result in '' (a string that matches everywhere), and we want it to match nowhere, so just use /^(?!)/ in that case (a regex that never matches).
Then get the .textContent of the target element, do the replacement (computing the replacement HTML dynamically to preserve the original case, and making sure to escape all HTML metacharacters properly), and put it back into .innerHTML.
Somewhat like this:
function highlight() {
const mapObj = {};
const whatInTextInput = document.querySelector(".toGetText").value;
const whatInTextInput_words_array = whatInTextInput.match(/\S+/g) || [];
const regex_str = whatInTextInput_words_array.sort(function (a, b) {
const na = a.length, nb = b.length;
return (
na > nb ? -1 :
na < nb ? 1 :
a < b ? -1 :
b < a ? 1 :
0
);
})
.map(s => s.replace(/\W/g, "\\$&"))
.join('|');
const regex = whatInTextInput_words_array.length ? new RegExp(regex_str, 'gi') : /^(?!)/;
const inputText = document.querySelector('.inputText');
const str = inputText.textContent;
inputText.innerHTML = str.replace(regex, function (word) {
return (
'<mark class="highlight">' +
word.replace(/[<>&]/g, c => '&#' + c.charCodeAt(0) + ';') + // hacky HTML escape
'</mark>'
);
});
}
.highlight {
background: yellow;
}
<button onclick="highlight()">Highlight</button>
<input type="text" class="toGetText"/>
<div class="inputText">
I have a cat, a dog, and a goat.
</div>

Bold part of String

What is the best way to bold a part of string in Javascript?
I have an array of objects. Each object has a name. There is also an input parameter.
If, for example, you write "sa" in input, it automatically searches in array looking for objects with names that contain "sa" string.
When I print all the names, I want to bold the part of the name that coincide with the input text.
For example, if I search for "Ma":
Maria
Amaria
etc...
I need a solution that doesn't use jQuery. Help is appreciated.
PD: The final strings are in the tag. I create a list using angular ng-repeat.
This is the code:
$scope.users = data;
for (var i = data.length - 1; i >= 0; i--) {
data[i].name=data[i].name.replace($scope.modelCiudad,"<b>"+$scope.modelCiudad+"</b>");
};
ModelCiudad is the input text content var. And data is the array of objects.
In this code if for example ModelCiudad is "ma" the result of each is:
<b>Ma</b>ria
not Maria
You can use Javascript's str.replace() method, where str is equal to all of the text you want to search through.
var str = "Hello";
var substr = "el";
str.replace(substr, '<b>' + substr + '</b>');
The above will only replace the first instance of substr. If you want to handle replacing multiple substrings within a string, you have to use a regular expression with the g modifier.
function boldString(str, substr) {
var strRegExp = new RegExp(substr, 'g');
return str.replace(strRegExp, '<b>'+substr+'</b>');
}
In practice calling boldString would looks something like:
boldString("Hello, can you help me?", "el");
// Returns: H<b>el</b>lo can you h<b>el</b>p me?
Which when rendered by the browser will look something like: Hello can you help me?
Here is a JSFiddle with an example: https://jsfiddle.net/1rennp8r/3/
A concise ES6 solution could look something like this:
const boldString = (str, substr) => str.replace(RegExp(substr, 'g'), `<b>${substr}</b>`);
Where str is the string you want to modify, and substr is the substring to bold.
ES12 introduces a new string method str.replaceAll() which obviates the need for regex if replacing all occurrences at once. It's usage in this case would look something like this:
const boldString = (str, substr) => str.replaceAll(substr, `<b>${substr}</b>`);
I should mention that in order for these latter approaches to work, your environment must support ES6/ES12 (or use a tool like Babel to transpile).
Another important note is that all of these approaches are case sensitive.
Here's a pure JS solution that preserves the original case (ignoring the case of the query thus):
const boldQuery = (str, query) => {
const n = str.toUpperCase();
const q = query.toUpperCase();
const x = n.indexOf(q);
if (!q || x === -1) {
return str; // bail early
}
const l = q.length;
return str.substr(0, x) + '<b>' + str.substr(x, l) + '</b>' + str.substr(x + l);
}
Test:
boldQuery('Maria', 'mar'); // "<b>Mar</b>ia"
boldQuery('Almaria', 'Mar'); // "Al<b>mar</b>ia"
I ran into a similar problem today - except I wanted to match whole words and not substrings. so if const text = 'The quick brown foxes jumped' and const word = 'foxes' than I want the result to be 'The quick brown <strong>foxes</strong> jumped'; however if const word = 'fox', than I expect no change.
I ended up doing something similar to the following:
const pattern = `(\\s|\\b)(${word})(\\s|\\b)`;
const regexp = new RegExp(pattern, 'ig'); // ignore case (optional) and match all
const replaceMask = `$1<strong>$2</strong>$3`;
return text.replace(regexp, replaceMask);
First I get the exact word which is either before/after some whitespace or a word boundary, and then I replace it with the same whitespace (if any) and word, except the word is wrapped in a <strong> tag.
Here is a version I came up with if you want to style words or individual characters at their index in react/javascript.
replaceAt( yourArrayOfIndexes, yourString/orArrayOfStrings )
Working example: https://codesandbox.io/s/ov7zxp9mjq
function replaceAt(indexArray, [...string]) {
const replaceValue = i => string[i] = <b>{string[i]}</b>;
indexArray.forEach(replaceValue);
return string;
}
And here is another alternate method
function replaceAt(indexArray, [...string]) {
const startTag = '<b>';
const endTag = '</b>';
const tagLetter = i => string.splice(i, 1, startTag + string[i] + endTag);
indexArray.forEach(tagLetter);
return string.join('');
}
And another...
function replaceAt(indexArray, [...string]) {
for (let i = 0; i < indexArray.length; i++) {
string = Object.assign(string, {
[indexArray[i]]: <b>{string[indexArray[i]]}</b>
});
}
return string;
}
Above solutions are great, but are limited! Imagine a test scenerio where you want to match case insensitive query in a string and they could be multiple matches.
For example
Query: ma
String: The Amazing Spiderman
Expected Result: The Amazing Spiderman
For above scenerio, use this:
const boldMatchText = (text,searchInput) => {
let str = text.toLowerCase();
const query = searchInput.toLowerCase();
let result = "";
let queryLoc = str.indexOf(query);
if (queryLoc === -1) {
result += text;
} else
do {
result += ` ${text.substr(0, queryLoc)}
<b>${text.substr(queryLoc, query.length)}</b>`;
str = str.substr(queryLoc + query.length, str.length);
text = text.substr(queryLoc + query.length, str.length);
queryLoc = str.indexOf(query);
} while (text.length > 0 && queryLoc !== -1);
return result + text;
};

Categories

Resources