javascript pass obj to string replace - javascript

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>

Related

Capital letters in a string using the 'map' method

I want to change to make the first letters of the words in the string uppercase, and after translating into an array, I use the map method. The problem is that inheritance does not work in this method, as I understand it, because when you return the map element, the original string is returned:
const str = 'dkdg gkdj wijoerbj'
let r = str.split(' ').map((item, index) => {
item[0] = item[0].toUpperCase()
return item
})
unchanged first letters will also be returned with such a code entry(is in the map()):
item[0] = item[0].toUpperCase()
return item[0]
however, when I return the first line, the letters still become uppercase, but I do not know how to return the rest of the word in this case(is in the map()):
return item[0] = item[0].toUpperCase()
why inheritance does not work as it should, tell me, please, and how to add the rest of the word if there are no other options?
You need to cancat the first letter with rest of the string. Also these two lines item[0] = item[0].toUpperCase();return item though will convert the first letter to uppercase but it will still return the original sting because item represent the original word nut not the modified text
const str = 'dkdg gkdj wijoerbj'
let r = str.split(' ').map((item, index) => {
return item.charAt(0).toUpperCase() + item.substring(1, item.length)
});
console.log(r)
We can do this with two array map methods as bellow.
const str = 'dkdg gkdj wijoerbj';
let r = str.split(' ').map((item, index) => {
return ([...item].map((letter, i) => {
return (i == 0) ? letter.toUpperCase() : letter;
})).join('');
});
console.log(r);
const str = 'dkdg gkdj wijoerbj'
let r = str.split(' ').map(x=> x[0].toLocaleUpperCase());
console.log(r)
u can try this code to get first letters of the words in the string uppercase into new arry using map and returns capital letter of each first letter

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)

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

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)

Make translation function not translate result again

I have made this very simplified version of a translation tool similar to Google Translate. The idea is to build this simple tool for a minority language in sweden called "jamska". The app is built up with a function that takes the string from a textarea with the ID #svenska and replaces words in the string using RegExp.
I've made an array called arr that's used in a for loop of the function as a dictionary. Each array item looks like this: var arr = [["eldröd", "eillrau"], ["oväder", "over"] ...]. The first word in each array item is in swedish, and the second word is in jamska. If the RegExp finds a matching word in the loop it replaces that word using this code:
function translate() {
var str = $("#svenska").val();
var newStr = "";
for (var i = 0; i < arr.length; i++) {
var replace = arr[i][0];
var replaceWith = arr[i][1];
var re = new RegExp('(^|[^a-z0-9åäö])' + replace + '([^a-z0-9åäö]|$)', 'ig');
str = str.replace(re, "$1" + replaceWith + '$2');
}
$("#jamska").val(str);
}
The translate() is then called in an event handler for when the #svenska textarea gets a keyup, like this: $("#svenska").keyup(function() { translate(); });
The translated string is then assigned as the value of another textarea with the ID #jamska. So far, so good.
I have a problem though: if the translated word in jamska also is a word in swedish, the function translates that word too. This problem is occurring because I'm assigning the variable str to the translated version of the same variable, using: str = str.replace(re, "$1" + replaceWith + '$2');. The function is using the same variable over and over again to perform the translation.
Example:
The swedish word "brydd" is "fel" in jamska. "Fel" is also a word in swedish, so the word that I get after the translation is "felht", since the swedish word "fel" is "felht" in jamska.
Does anyone have any idea for how to work around this problem?
Instead of looking for each Jamska word in the input and replacing them with the respective translation, I would recommend to find any word ([a-z0-9åäö]+) in your text and replace this word either with its translation if one is found in the dictionary or with itself otherwise:
//var arr = [["eldröd", "eillrau"], ["oväder", "over"] ...]
// I'd better use dictionary instead of array to define your dictionary
var dict = {
eldröd: "oväder",
eillrau: "over"
// ...
};
var str = "eldröd test eillrau eillrau oväder over";
var translated = str.replace(/[a-z0-9åäö]+/ig, function(m) {
var word = m.toLowerCase();
var trans = dict[word];
return trans === undefined ? word : trans;
});
console.log(translated);
Update:
If dictionary keys may be represented by phrases (i.e. technically appear as strings with spaces), the regex should be extended to include all these phrases explicitly. So the final regex would look like
(?:phrase 1|phrase 2|etc...)(?![a-z0-9åäö])|[a-z0-9åäö]+
It will try to match one of the phrases explicitly first and only then single words. The (?![a-z0-9åäö]) lookbehind helps to filter out phrases immediately followed by letters (e.g. varken bättre eller sämreåäö).
Phrases immediately preceded by letters are implicitly filtered out by the fact that a match is either the fist one (and therefore is not preceded by any letter) or it's not the first and therefore the previous one is separated from the current by some spaces.
//var arr = [["eldröd", "eillrau"], ["oväder", "over"] ...]
// I'd better use dictionary instead of array to define your dictionary
var dict = {
eldröd: "oväder",
eillrau: "over",
bättre: "better",
"varken bättre eller sämre": "vär å int viller",
"test test": "double test"
// ...
};
var str = "eldröd test eillrau eillrau oväder over test test ";
str += "varken bättre eller sämre ";
str += "don't trans: varken bättre eller sämreåäö";
str += "don't trans again: åäövarken bättre eller sämre";
var phrases = Object.keys(dict)
.filter(function(k) { return /\s/.test(k); })
.sort(function(a, b) { return b.length - a.length; })
.join('|');
var re = new RegExp('(?:' + phrases + ')(?![a-z0-9åäö])|[a-z0-9åäö]+', 'ig');
var translated = str.replace(re, function(m) {
var word = m.toLowerCase();
var trans = dict[word];
return trans === undefined ? word : trans;
});
console.log(translated);

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