I have a set of strings that I need to replace, but I need to keep the case of letters.
Both the input words and output words are of the same length.
For example, if I need to replace "abcd" with "qwer", then the following should happen:
"AbcD" translates to "QweR"
"abCd" translates to "qwEr"
and so on.
Right now I'm using JavaScript's replace, but capital letters are lost on translation.
r = new RegExp( "(" + 'asdf' + ")" , 'gi' );
"oooAsdFoooo".replace(r, "qwer");
Any help would be appreciated.
Here’s a helper:
function matchCase(text, pattern) {
var result = '';
for(var i = 0; i < text.length; i++) {
var c = text.charAt(i);
var p = pattern.charCodeAt(i);
if(p >= 65 && p < 65 + 26) {
result += c.toUpperCase();
} else {
result += c.toLowerCase();
}
}
return result;
}
Then you can just:
"oooAsdFoooo".replace(r, function(match) {
return matchCase("qwer", match);
});
I'll leave this here for reference.
Scenario: case-insensitive search box on list of items, partial match on string should be displayed highlighted but keeping original case.
highlight() {
const re = new RegExp(this.searchValue, 'gi'); // global, insensitive
const newText = name.replace(re, `<b>$&</b>`);
return newText;
}
the $& is the matched text with case
String.prototype.translateCaseSensitive = function (fromAlphabet, toAlphabet) {
var fromAlphabet = fromAlphabet.toLowerCase(),
toAlphabet = toAlphabet.toLowerCase(),
re = new RegExp("[" + fromAlphabet + "]", "gi");
return this.replace(re, function (char) {
var charLower = char.toLowerCase(),
idx = fromAlphabet.indexOf(charLower);
if (idx > -1) {
if (char === charLower) {
return toAlphabet[idx];
} else {
return toAlphabet[idx].toUpperCase();
}
} else {
return char;
}
});
};
and
"AbcD".translateCaseSensitive("abcdefg", "qwertyu")
will return:
"QweR"
Here's a replaceCase function:
We turn the input pattern into a regular expression
We have a nested replacer function which iterates through every character
We use regular expression /[A-Z]/ to identify capital letters, otherwise we assume everything is in lowercase
function replaceCase(str, pattern, newStr) {
const rx = new RegExp(pattern, "ig")
const replacer = (c, i) => c.match(/[A-Z]/) ? newStr[i].toUpperCase() : newStr[i]
return str.replace(rx, (oldStr) => oldStr.replace(/./g, replacer) )
}
let out = replaceCase("This is my test string: AbcD", "abcd", "qwer")
console.log(out) // This is my test string: QweR
out = replaceCase("This is my test string: abCd", "abcd", "qwer")
console.log(out) // This is my test string: qwEr
You could create your own replace function such as
if(!String.prototype.myreplace){
String.prototype.myreplace = (function(obj){
return this.replace(/[a-z]{1,1}/gi,function(a,b){
var r = obj[a.toLowerCase()] || a;
return a.charCodeAt(0) > 96? r.toLowerCase() : r.toUpperCase();
});
});
}
This takes in a object that maps different letters. and it can be called such as follows
var obj = {a:'q',b:'t',c:'w'};
var s = 'AbCdea';
var n = s.myreplace(obj);
console.log(n);
This means you could potentially pass different objects in with different mappings if need be. Here's a simple fiddle showing an example (note the object is all lowercase but the function itself looks at case of the string as well)
Expanding on Ryan O'Hara's answer, the below solution avoids using charCodes and the issues that maybe encountered in using them. It also ensures the replacement is complete when the strings are of different lengths.
function enforceLength(text, pattern, result) {
if (text.length > result.length) {
result = result.concat(text.substring(result.length, text.length));
}
if (pattern.length > text.length) {
result = result.substring(0, text.length);
}
return result;
}
function matchCase(text, pattern){
var result = '';
for (var i =0; i < pattern.length; i++){
var c = text.charAt(i);
var p = pattern.charAt(i);
if(p === p.toUpperCase()) {
result += c.toUpperCase();
} else {
result += c.toLowerCase();
}
}
return enforceLength(text, pattern, result);
}
This should replace while preserving the case. Please let me know if anyone finds any flaws in this solution. I hope this helps. Thank-you!
function myReplace(str, before, after) {
var match=function(before,after){
after=after.split('');
for(var i=0;i<before.length;i++)
{
if(before.charAt(i)==before[i].toUpperCase())
{
after[i]=after[i].toUpperCase();
}
else if(before.charAt(i)==before[i].toLowerCase())
{
after[i]=after[i].toLowerCase();
}
return after.join('');
}
};
console.log(before,match(before,after));
str =str.replace(before,match(before,after));
return str;
}
myReplace("A quick brown fox jumped over the lazy dog", "jumped", "leaped");
I had a sentence where I had to replace each word with another word and that word can be longer/shorter than the word its replacing so its similar to the question but instead of a fixed length, they're dynamic.
My solution
For simplicity, I am focusing on a single word.
const oldWord = "tEsT";
const newWord = "testing";
Split both words so that I can iterate over each individual letters.
const oldWordLetters = oldWord.split("");
const newWordLetters = newWord.split("");
Now, I would iterate over the newWord letters and use its index to then get the corresponding oldWord letter in the same position. Then I would check if the old letter is capital and if it is then make the new letter in the same position capital as well.
for (const [i, letter] of newWordLetters.entries()) {
const oldLetter = oldWordLetters[i];
// stop iterating if oldWord is shorter (not enough letters to copy case).
if (!oldLetter) {
break;
}
const isCapital = oldLetter === oldLetter.toUpperCase();
// make the new letter in the same position as the old letter capital
if (isCapital) {
newWordLetters[i] = letter.toUpperCase();
}
}
The final world would be tEsTing after joining the letters again.
const finalWord = newWordLetters.join("");
console.log(finalWord); // "tEsTing"
Full code
const oldWord = "tEsT";
const newWord = "testing";
const oldWordLetters = oldWord.split("");
const newWordLetters = newWord.split("");
for (const [i, letter] of newWordLetters.entries()) {
const oldLetter = oldWordLetters[i];
// stop iterating if oldWord is shorter (not enough letters to copy case).
if (!oldLetter) {
break;
}
const isCapital = oldLetter === oldLetter.toUpperCase();
// make the new letter in the same position as the old letter capital
if (isCapital) {
newWordLetters[i] = letter.toUpperCase();
}
}
const finalWord = newWordLetters.join("");
console.log(finalWord);
I think this could work
function formatItem(text, searchText){
const search = new RegExp(escapeRegExp(searchText), 'iu')
return text?.toString().replace(search, (m) => `<b>${m}</b>`)
}
function escapeRegExp(text) {
return text?.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') ?? '';
}
Thank you for asking this. I had the same problem when I wanted to search text and replace certain words with links, which was a slightly more specific situation because it is replacing text strings with html strings. I'll put my solution here in case anyone who finds this is doing anything similar.
let elmt = document.getElementById('the-element');
let a = document.createElement('a');
a.href = "https://www.example.com";
let re = new RegExp('the string to find', 'gi');
elmt.innerHTML = elmt.innerHTML.replaceAll(re, function (match) {
a.innerText = match;
return a.outerHTML;
});
So the regular expression ensures that it searches for case-insensitive matches, and the function as the second argument of the replaceAll function specifies that it is supposed to set the innerText of the new tag equal to the old string verbatim, before then returning the outerHTML of the whole tag.
Here is a replaceAllCaseSensitive function. If your want, you can change replaceAll by replace.
const replaceAllCaseSensitive = (
text, // Original string
pattern, // RegExp with the pattern you want match. It must include the g (global) and i (case-insensitive) flags.
replacement // string with the replacement
) => {
return text.replaceAll(pattern, (match) => {
return replacement
.split("")
.map((char, i) =>
match[i] === match[i].toUpperCase() ? char.toUpperCase() : char
)
.join("");
});
};
console.log(replaceAllCaseSensitive("AbcD abCd", /abcd/gi, "qwer"));
// outputs "QweR qwEr"
console.log(replaceAllCaseSensitive("AbcD abCd", /abcd/gi, "qwe"));
// outputs "Qwe qwE"
The function works even if replacement is shorter than match.
Related
I am trying to create a function that prints each word on a new line. The argument given is a string with words that aren't separated by a space but capitalized except the first word i.e. "helloMyNameIsMark". I have something that works but wondering if there's a better way of doing this in javaScript.
separateWords = (string) => {
const letters = string.split('');
let word = "";
const words = letters.reduce((acc, letter, idx) => {
if (letter === letter.toUpperCase()) {
acc.push(word);
word = "";
word = word.concat(letter);
} else if (idx === letters.length - 1) {
word = word.concat(letter);
acc.push(word);
} else {
word = word.concat(letter)
}
return acc
}, []);
words.forEach(word => {
console.log(word)
})
}
You could use the regex [A-Z] and replace each upper case letter with \n prefix
const separateWords = str => str.replace(/[A-Z]/g, m => '\n' + m)
console.log(separateWords('helloMyNameIsMark'))
Or you could use a lookahead (?=[A-Z]) to split at each upper case letter to get an array of words. Then loop through the array to log each word:
const separateWords = str => str.split(/(?=[A-Z])/g)
separateWords('helloMyNameIsMark').forEach(w => console.log(w))
I would separate the breaking of words into an array and the printing of that array into two distinct functions. Regular expressions make that first part much easier than your reduce call. (But that reduce is a good thought if you don't see a regex solution.)
My version might look like this:
const separateWords = (str) => str .replace (/([A-Z])/g, " $1") .split (' ')
const printSeparateWords = (str) => separateWords (str) .forEach (word => console.log (word) )
printSeparateWords ("helloMyNameIsMark")
Very similar to adiga's answer, but it can actually be simpler:
const separateWords = str => str.replace(/[A-Z]/g, '\n$&');
This will also benefit from improved performance (might matter if used at scale).
Here's a more literal interpretation of your stated requirement: prints each word on a new line.
function separateWords(str){
let currentWord = '';
for (let chr of str){
if (chr == chr.toUpperCase()){
console.log(currentWord);
currentWord = chr;
} else {
currentWord += chr;
}
}
if (currentWord)
console.log(currentWord);
}
separateWords('helloMyNameIsMark');
I'm trying to create a function which will search a specific words.
For example if I tap the letter "O" the first result will be the word "Oasis" (and not "U2 - Still Haven't Found What I'm..." and all the other songs that has "O" in their words), or any other middle word. It need to ignore spaces - " ", the char "-" (" "-" ") and ' or other chars, so it will connect all thw words together. For example when I tap "dontlookbackin" the search will find the word which is starting with the first letter I pressed, than the other letter in the word and so on.
So far I'm only able to make a simple search - to find every word that contains the first letter I pressed.
The specific part of the "search" function :
SearchSong = () => {
console.log("Pressed")
var e = this.state.searchRes;
if (e == "") {
return;
}
var musicList = [];
var songs = [];
if (this.state.songs != null) {
songs = this.state.songs;
musicList = songs.filter(song =>
song.Song_Name.toLowerCase().includes(e.toLowerCase())
);
Example of the current search -
If more detials and fixes are needed please tell me.
let strKeyword = "string001";
musicList = [];
songsLength = songs.length;
for (i = 0; i < songsLength; i++) {
if (songs[i].Song_Name.replace(/[^a-zA-Z0-9]/g, "").substr(0, strKeyword.length).toLowerCase() == strKeyword.toLowerCase()) {
musicList.push(songs[i]);
}
}
console.log(musicList);
Something along these lines should get you started (see comments):
function removeUnwantedChars(str) {
return str.replace(/\W/g, "");
}
function search(str) {
// Create regex like "abc" becomes "^a.*b.*c"
str = removeUnwantedChars(str);
if (!str) {
return [];
}
const searchRex = new RegExp(
"^" +
RegExp.escape(str.charAt(0)) + [...str.substring(1)].map(ch =>
".*" + RegExp.escape(ch)
).join(""),
"i"
);
// Search for matches
return songs.filter(song => searchRex.test(song.Song_Name.replace(/\W/g, "")));
}
...using a regex escape function along the lines of the ones in this question's answers.
Live Example:
if (!RegExp.escape) {
RegExp.escape = function(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
};
}
const songs = [
{Song_Name: "U2 - I Still Haven't Found What I'm Looking For"},
{Song_Name: "Oasis - Champagne Supernova"},
{Song_Name: "Oasis - Don't Look Back In Anger"},
{Song_Name: "The Rolling Stones - Like A Rolling Stone"}
];
function removeUnwantedChars(str) {
return str.replace(/\W/g, "");
}
function search(str) {
// Create regex like "abc" becomes "^a.*b.*c"
str = removeUnwantedChars(str);
if (!str) {
return [];
}
const searchRex = new RegExp(
"^" +
RegExp.escape(str.charAt(0)) + [...str.substring(1)].map(ch =>
".*" + RegExp.escape(ch)
).join(""),
"i"
);
// Search for matches
return songs.filter(song => searchRex.test(song.Song_Name.replace(/\W/g, "")));
}
const testStrings = [
"oasisdontlookbackin",
"oasisanger",
"therollstone",
"rolling" // no match
];
for (const str of testStrings) {
console.log(str, "=>", search(str));
}
.as-console-wrapper {
max-height: 100% !important;
}
The .* in the regex means "any number of any character here" which means that you don't have to type every significant character after the first one (which is why "oasisanger" works). But it does incorporate your requirement that the first character match the first character of the name.
Try this:
song.Song_Name.toLowerCase().startsWith(e.toLowerCase())
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)
I have a string, let's say Hello world and I need to replace the char at index 3. How can I replace a char by specifying a index?
var str = "hello world";
I need something like
str.replaceAt(0,"h");
In JavaScript, strings are immutable, which means the best you can do is to create a new string with the changed content and assign the variable to point to it.
You'll need to define the replaceAt() function yourself:
String.prototype.replaceAt = function(index, replacement) {
return this.substring(0, index) + replacement + this.substring(index + replacement.length);
}
And use it like this:
var hello = "Hello World";
alert(hello.replaceAt(2, "!!")); // He!!o World
There is no replaceAt function in JavaScript. You can use the following code to replace any character in any string at specified position:
function rep() {
var str = 'Hello World';
str = setCharAt(str,4,'a');
alert(str);
}
function setCharAt(str,index,chr) {
if(index > str.length-1) return str;
return str.substring(0,index) + chr + str.substring(index+1);
}
<button onclick="rep();">click</button>
You can't. Take the characters before and after the position and concat into a new string:
var s = "Hello world";
var index = 3;
s = s.substring(0, index) + 'x' + s.substring(index + 1);
str = str.split('');
str[3] = 'h';
str = str.join('');
There are lot of answers here, and all of them are based on two methods:
METHOD1: split the string using two substrings and stuff the character between them
METHOD2: convert the string to character array, replace one array member and join it
Personally, I would use these two methods in different cases. Let me explain.
#FabioPhms: Your method was the one I initially used and I was afraid that it is bad on string with lots of characters. However, question is what's a lot of characters? I tested it on 10 "lorem ipsum" paragraphs and it took a few milliseconds. Then I tested it on 10 times larger string - there was really no big difference. Hm.
#vsync, #Cory Mawhorter: Your comments are unambiguous; however, again, what is a large string? I agree that for 32...100kb performance should better and one should use substring-variant for this one operation of character replacement.
But what will happen if I have to make quite a few replacements?
I needed to perform my own tests to prove what is faster in that case. Let's say we have an algorithm that will manipulate a relatively short string that consists of 1000 characters. We expect that in average each character in that string will be replaced ~100 times. So, the code to test something like this is:
var str = "... {A LARGE STRING HERE} ...";
for(var i=0; i<100000; i++)
{
var n = '' + Math.floor(Math.random() * 10);
var p = Math.floor(Math.random() * 1000);
// replace character *n* on position *p*
}
I created a fiddle for this, and it's here.
There are two tests, TEST1 (substring) and TEST2 (array conversion).
Results:
TEST1: 195ms
TEST2: 6ms
It seems that array conversion beats substring by 2 orders of magnitude! So - what the hell happened here???
What actually happens is that all operations in TEST2 are done on array itself, using assignment expression like strarr2[p] = n. Assignment is really fast compared to substring on a large string, and its clear that it's going to win.
So, it's all about choosing the right tool for the job. Again.
Work with vectors is usually most effective to contact String.
I suggest the following function:
String.prototype.replaceAt=function(index, char) {
var a = this.split("");
a[index] = char;
return a.join("");
}
Run this snippet:
String.prototype.replaceAt=function(index, char) {
var a = this.split("");
a[index] = char;
return a.join("");
}
var str = "hello world";
str = str.replaceAt(3, "#");
document.write(str);
In Javascript strings are immutable so you have to do something like
var x = "Hello world"
x = x.substring(0, i) + 'h' + x.substring(i+1);
To replace the character in x at i with 'h'
function dothis() {
var x = document.getElementById("x").value;
var index = document.getElementById("index").value;
var text = document.getElementById("text").value;
var length = document.getElementById("length").value;
var arr = x.split("");
arr.splice(index, length, text);
var result = arr.join("");
document.getElementById('output').innerHTML = result;
console.log(result);
}
dothis();
<input id="x" type="text" value="White Dog" placeholder="Enter Text" />
<input id="index" type="number" min="0"value="6" style="width:50px" placeholder="index" />
<input id="length" type="number" min="0"value="1" style="width:50px" placeholder="length" />
<input id="text" type="text" value="F" placeholder="New character" />
<br>
<button id="submit" onclick="dothis()">Run</button>
<p id="output"></p>
This method is good for small length strings but may be slow for larger text.
var x = "White Dog";
var arr = x.split(""); // ["W", "h", "i", "t", "e", " ", "D", "o", "g"]
arr.splice(6, 1, 'F');
/*
Here 6 is starting index and 1 is no. of array elements to remove and
final argument 'F' is the new character to be inserted.
*/
var result = arr.join(""); // "White Fog"
One-liner using String.replace with callback (no emoji support):
// 0 - index to replace, 'f' - replacement string
'dog'.replace(/./g, (c, i) => i == 0? 'f': c)
// "fog"
Explained:
//String.replace will call the callback on each pattern match
//in this case - each character
'dog'.replace(/./g, function (character, index) {
if (index == 0) //we want to replace the first character
return 'f'
return character //leaving other characters the same
})
Generalizing Afanasii Kurakin's answer, we have:
function replaceAt(str, index, ch) {
return str.replace(/./g, (c, i) => i == index ? ch : c);
}
let str = 'Hello World';
str = replaceAt(str, 1, 'u');
console.log(str); // Hullo World
Let's expand and explain both the regular expression and the replacer function:
function replaceAt(str, index, newChar) {
function replacer(origChar, strIndex) {
if (strIndex === index)
return newChar;
else
return origChar;
}
return str.replace(/./g, replacer);
}
let str = 'Hello World';
str = replaceAt(str, 1, 'u');
console.log(str); // Hullo World
The regular expression . matches exactly one character. The g makes it match every character in a for loop. The replacer function is called given both the original character and the index of where that character is in the string. We make a simple if statement to determine if we're going to return either origChar or newChar.
var str = "hello world";
console.log(str);
var arr = [...str];
arr[0] = "H";
str = arr.join("");
console.log(str);
This works similar to Array.splice:
String.prototype.splice = function (i, j, str) {
return this.substr(0, i) + str + this.substr(j, this.length);
};
You could try
var strArr = str.split("");
strArr[0] = 'h';
str = strArr.join("");
this is easily achievable with RegExp!
const str = 'Hello RegEx!';
const index = 11;
const replaceWith = 'p';
//'Hello RegEx!'.replace(/^(.{11})(.)/, `$1p`);
str.replace(new RegExp(`^(.{${ index }})(.)`), `$1${ replaceWith }`);
//< "Hello RegExp"
Using the spread syntax, you may convert the string to an array, assign the character at the given position, and convert back to a string:
const str = "hello world";
function replaceAt(s, i, c) {
const arr = [...s]; // Convert string to array
arr[i] = c; // Set char c at pos i
return arr.join(''); // Back to string
}
// prints "hallo world"
console.log(replaceAt(str, 1, 'a'));
You could try
var strArr = str.split("");
strArr[0] = 'h';
str = strArr.join("");
Check out this function for printing steps
steps(3)
// '# '
// '## '
// '###'
function steps(n, i = 0, arr = Array(n).fill(' ').join('')) {
if (i === n) {
return;
}
str = arr.split('');
str[i] = '#';
str = str.join('');
console.log(str);
steps(n, (i = i + 1), str);
}
#CemKalyoncu: Thanks for the great answer!
I also adapted it slightly to make it more like the Array.splice method (and took #Ates' note into consideration):
spliceString=function(string, index, numToDelete, char) {
return string.substr(0, index) + char + string.substr(index+numToDelete);
}
var myString="hello world!";
spliceString(myString,myString.lastIndexOf('l'),2,'mhole'); // "hello wormhole!"
If you want to replace characters in string, you should create mutable strings. These are essentially character arrays. You could create a factory:
function MutableString(str) {
var result = str.split("");
result.toString = function() {
return this.join("");
}
return result;
}
Then you can access the characters and the whole array converts to string when used as string:
var x = MutableString("Hello");
x[0] = "B"; // yes, we can alter the character
x.push("!"); // good performance: no new string is created
var y = "Hi, "+x; // converted to string: "Hi, Bello!"
You can extend the string type to include the inset method:
String.prototype.append = function (index,value) {
return this.slice(0,index) + value + this.slice(index);
};
var s = "New string";
alert(s.append(4,"complete "));
Then you can call the function:
You can concatenate using sub-string function at first select text before targeted index and after targeted index then concatenate with your potential char or string. This one is better
const myString = "Hello world";
const index = 3;
const stringBeforeIndex = myString.substring(0, index);
const stringAfterIndex = myString.substring(index + 1);
const replaceChar = "X";
myString = stringBeforeIndex + replaceChar + stringAfterIndex;
console.log("New string - ", myString)
or
const myString = "Hello world";
let index = 3;
myString = myString.substring(0, index) + "X" + myString.substring(index + 1);
I did a function that does something similar to what you ask, it checks if a character in string is in an array of not allowed characters if it is it replaces it with ''
var validate = function(value){
var notAllowed = [";","_",">","<","'","%","$","&","/","|",":","=","*"];
for(var i=0; i<value.length; i++){
if(notAllowed.indexOf(value.charAt(i)) > -1){
value = value.replace(value.charAt(i), "");
value = validate(value);
}
}
return value;
}
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;
}
Here is my solution using the ternary and map operator. More readable, maintainable end easier to understand if you ask me.
It is more into es6 and best practices.
function replaceAt() {
const replaceAt = document.getElementById('replaceAt').value;
const str = 'ThisIsATestStringToReplaceCharAtSomePosition';
const newStr = Array.from(str).map((character, charIndex) => charIndex === (replaceAt - 1) ? '' : character).join('');
console.log(`New string: ${newStr}`);
}
<input type="number" id="replaceAt" min="1" max="44" oninput="replaceAt()"/>
My safe approach with negative indexes
/**
* #param {string} str
* #param {number} index
* #param {string} replacement
* #returns {string}
*/
static replaceAt (str, index, replacement)
{
if (index < 0) index = str.length + index
if (index < 0 || index >= str.length) throw new Error(`Index (${index}) out of bounds "${str}"`)
return str.substring(0, index) + replacement + str.substring(index + 1)
}
Use it like that:
replaceAt('my string', -1, 'G') // 'my strinG'
replaceAt('my string', 2, 'yy') // 'myyystring'
replaceAt('my string', 22, 'yy') // Uncaught Error: Index (22) out of bounds "my string"
Lets say you want to replace Kth index (0-based index) with 'Z'.
You could use Regex to do this.
var re = var re = new RegExp("((.){" + K + "})((.){1})")
str.replace(re, "$1A$`");
You can use the following function to replace Character or String at a particular position of a String. To replace all the following match cases use String.prototype.replaceAllMatches() function.
String.prototype.replaceMatch = function(matchkey, replaceStr, matchIndex) {
var retStr = this, repeatedIndex = 0;
for (var x = 0; (matchkey != null) && (retStr.indexOf(matchkey) > -1); x++) {
if (repeatedIndex == 0 && x == 0) {
repeatedIndex = retStr.indexOf(matchkey);
} else { // matchIndex > 0
repeatedIndex = retStr.indexOf(matchkey, repeatedIndex + 1);
}
if (x == matchIndex) {
retStr = retStr.substring(0, repeatedIndex) + replaceStr + retStr.substring(repeatedIndex + (matchkey.length));
matchkey = null; // To break the loop.
}
}
return retStr;
};
Test:
var str = "yash yas $dfdas.**";
console.log('Index Matched replace : ', str.replaceMatch('as', '*', 2) );
console.log('Index Matched replace : ', str.replaceMatch('y', '~', 1) );
Output:
Index Matched replace : yash yas $dfd*.**
Index Matched replace : yash ~as $dfdas.**
I se this to make a string proper case, that is, the first letter is Upper Case and all the rest are lower case:
function toProperCase(someString){
return someString.charAt(0).toUpperCase().concat(someString.toLowerCase().substring(1,someString.length));
};
This first thing done is to ensure ALL the string is lower case - someString.toLowerCase()
then it converts the very first character to upper case -someString.charAt(0).toUpperCase()
then it takes a substring of the remaining string less the first character -someString.toLowerCase().substring(1,someString.length))
then it concatenates the two and returns the new string -someString.charAt(0).toUpperCase().concat(someString.toLowerCase().substring(1,someString.length))
New parameters could be added for the replacement character index and the replacement character, then two substrings formed and the indexed character replaced then concatenated in much the same way.
The solution does not work for negative index so I add a patch to it.
String.prototype.replaceAt=function(index, character) {
if(index>-1) return this.substr(0, index) + character + this.substr(index+character.length);
else return this.substr(0, this.length+index) + character + this.substr(index+character.length);
}
"hello world".replace(/(.{3})./, "$1h")
// 'helho world'
The methods on here are complicated.
I would do it this way:
var myString = "this is my string";
myString = myString.replace(myString.charAt(number goes here), "insert replacement here");
This is as simple as it gets.
How can I test if a RegEx matches a string exactly?
var r = /a/;
r.test("a"); // returns true
r.test("ba"); // returns true
testExact(r, "ba"); // should return false
testExact(r, "a"); // should return true
Either modify the pattern beforehand so that it only matches the entire string:
var r = /^a$/
or check afterward whether the pattern matched the whole string:
function matchExact(r, str) {
var match = str.match(r);
return match && str === match[0];
}
Write your regex differently:
var r = /^a$/;
r.test('a'); // true
r.test('ba'); // false
If you do not use any placeholders (as the "exactly" seems to imply), how about string comparison instead?
If you do use placeholders, ^ and $ match the beginning and the end of a string, respectively.
In case anyone receives an error like
Syntax Error: Invalid regular expression
by using the .match() function. You could always go back to the roots:
!!note this code is for matchin an exact string, if you want to search for an exact phrase in a string, you should filter it before hand
console.log("Exact data found: ", hasExactString("?hello", "?hello"))
console.log("Exact data found: ", hasExactString("?hello", "?helloBye"))
function hasExactString(data, searchTerm) {
console.log("search for ", searchTerm, " in ", data);
data = data.toLowerCase(); //if search term should be case insensitive
const searchExpressionLength = searchTerm.length;
const dataInputLength = data.length;
if (dataInputLength != searchExpressionLength) {
return false;
}
else {
//search letter by letter -back to the roots
for (var i = 0; i < searchExpressionLength; i++) {
if (data[i] != searchTerm[i]) {
return false;
}
}
return true;
}
}
...13 years late, but nonetheless^^
var data = {"values": [
{"name":0,"value":0.12791263050161572},
{"name":1,"value":0.13158780927382124}
]};
//JSON to string conversion
var a = JSON.stringify(data);
// replace all name with "x"- global matching
var t = a.replace(/name/g,"x");
// replace exactly the value rather than all values
var d = t.replace(/"value"/g, '"y"');
// String to JSON conversion
var data = JSON.parse(d);
Here's what is (IMO) by far the best solution in one line, per modern javascript standards:
const str1 = 'abc';
const str2 = 'abc';
return (str1 === str2); // true
const str1 = 'abcd';
const str2 = 'abc';
return (str1 === str2); // false
const str1 = 'abc';
const str2 = 'abcd';
return (str1 === str2); // false