简体   繁体   中英

Replace word in string in a specific position

How can I replace a string in a paragraph with another string without using the .replace() method of javascript, and the reason I can not use the .replace() method is because it makes the replacement only with the first match of the word in the string, To put in context a little bit more why i can not use .replace() , im trying the create a marking function where the user is gonna be able to marker text in a document, the marker is gonna be storage on the server side, So i request for the markers of the documents, the marker object contain the start and the end position of the selection in the string, so i take those to values to extract the text and wrapped in to a mark tag, after that i need to replace the text already with the mark tag in to the original text, but there is the case where the user can select a word that is more than one time in the document, so i can not use replace, so i need to use the start and end position of the selection to .replace() the text, for example:

Lorem ipsum dolor sit amet, consectetur Lorem adipiscing elit. Vivamus ipsum Lorem eros, interdum ac cursus et, pulvinar Lorem at est. Integer nec rutrum ligula. In dignissim est a elit porttitor finibus.

and I want to replace the 3th 'Lorem' for 'Space' without touch the rest i can not use .replace() . Is there any other way to this, maybe using start and end position of a cursor selection?

  /**
  * Count the position and the number of the html tags
  * to position the marker.
  * @param {object} The chapter object
  * @param {object} The marker object
  * @return {object} Return the chapter with the marker on it.
  */
  public characterCounter(chapter, marker): any {
    const counters: any = {
      htmlSimbol: null, // < or >
      characters: 0,
      startTags: 0, // count html until the start of the selection.
      endTags: 0 // count html until the end of the selection.
    };

    /* Here  we loop the blocks to find the righ
    *  one to place marker.
    */
    chapter.blocks.some((block) => {
      if (marker.blockId === block.blockId) {
        /*
        * Ones the block is founded start counting the
        * characters from the string.
        */
        const blockText = block.blockElement.text;
        for (let i = 0; i < blockText.length; i++) {
          /* when the loop reach the end of the selection
          * and match with the number of characters counted
          * that are not part of html tag the loop stop.
          */
          if (counters.characters === marker.end) {
            break;
          } else {
            /*
            * if the loop crash wiht < then start counting
            * characters as part of the html tags and stop
            * when crash with >
            */
            if (blockText[i] === '<' || blockText[i] === '>') {
              counters.htmlSimbol = blockText[i];
            } else if (blockText[i] !== '<' && blockText[i] !== '>' && counters.htmlSimbol !== null) {
              if (counters.htmlSimbol === '<') {
                if (counters.characters <= marker.start) {
                  counters.startTags += 1; // count the characters from the open html tags founded
                } else {
                  counters.endTags += 1; // count the characters from the close html tags founded
                }
              } else if (counters.htmlSimbol === '>') {
                if (counters.characters <= marker.start) {
                  counters.startTags += 2;  // count the characters from the open html tags founded
                } else {
                  counters.endTags += 2; // count the characters from the close html tags founded
                }
                counters.htmlSimbol = null; // change to null to know that is the end ot he html tag
              }
            } else if (counters.htmlSimbol === null) {
              counters.characters += 1; // count the characters that are not part of html tag ( only text no html).
            }
          }
        }

        // Here is where i extract the text selected and wrapped in to the mark tag to then replaced on the original block of text

        counters.endTags += counters.startTags;
        marker.start += counters.startTags;
        marker.end += counters.endTags;

        const textSelected = blockText.substring(marker.start, marker.end);

        const markerText = `<mark class="${marker.color}" *ngClass=""  alt="${marker.id}">${textSelected}</mark>`;

        block.blockElement.text = blockText.replace(textSelected, markerText);

        return false;
      }
    });
    return chapter;
  }

  /**
  * Analyze the text block and add the marking
  * to the right block of text.
  * @param {array} The list of markers
  * @param {array} The array of blocks
  * @return {array} the array of block with the markers.
  */
  public addMarking(markersList, chaptersArray): any {
    let index = 0;
    markersList.some((marker) => {
      index = chaptersArray.map((e) => e.id).indexOf(marker.chapterId);
      if (index > -1) {
        this.characterCounter(chaptersArray[index], marker);
        return false;
      } else {
        chaptersArray.some((chapter) => {
          index = chapter.subChapters.map((e) => e.id).indexOf(marker.chapterId);
          if (index > -1) {
            this.characterCounter(chapter.subChapters[index], marker);
            return false;
          }
        });
      }
    });
    return chaptersArray;
  }

the problem is after i have the mark tag applied, after i apply the mark tag to the selected text, i need to replace the original text with the one with mark tag, as you can see in the code im using .replace() , so i have the problem that the user select a word the is more than one time in a long paragraph so the text is replaced on the wrong place.

I tried to implement a solution without using replacement, but rather iterating once over the markers and the original text, and in the meantime building the "new text" with markers applied. For simplicity, I used the tag mark , hard coded in the solution. You could fairly easily parametrize this aspect.

 // suppose we are given the original text, and the markers as start-end pairs of selection character positions const text = `Lorem ipsum dolor sit amet, consectetur Lorem adipiscing elit. Vivamus ipsum Lorem eros, interdum ac cursus et, pulvinar Lorem at est. Integer nec rutrum ligula. In dignissim est a elit porttitor finibus.`; const markers = [{start: 77, end: 82}, {start: 40, end: 45}]; // positions defining the 2nd and the 3rd occurence of Lorem, chosen arbitrarily function markSelections(text, markers) { // sort markers just in case the are not sorted from the Storage const sortedMarkers = [...markers].sort((m1, m2) => m1.start - m2.start); let markedText = ''; let characterPointer = 0; sortedMarkers.forEach(({start, end}) => { markedText += text.substring(characterPointer, start); markedText += '<mark>'; markedText += text.substring(start, end); markedText += '</mark>'; characterPointer = end; }); // add the remaining text after markers markedText += text.substring(characterPointer); return markedText; } document.write(markSelections(text, markers)); 
 mark { color: red; } 

You can do that by using the following regular expression :

/(?!^)lorem/gi

And use a callback function in the String.prototype.replace function. That callback function will increment the previousMatches variable and in case it matches de specificed position , it will return the replacement.

In the snippet, I'm only replacing the 3rd occurence :

 var originalContent, searchedWord, replacement, pattern, regExp, newContent, previousMatches = 0; originalContent = 'Lorem ipsum dolor sit amet, consectetur Lorem adipiscing elit. Vivamus ipsum Lorem eros, interdum ac cursus et, pulvinar Lorem at est. Integer nec rutrum ligula. In dignissim est a elit porttitor finibus. Lorem ipsum dolor sit amet, consectetur Lorem adipiscing elit. Vivamus ipsum Lorem eros, interdum ac cursus et, pulvinar Lorem at est. Integer nec rutrum ligula. In dignissim est a elit porttitor finibus.'; document.write("Before : <br/> " + originalContent); searchedWord = "Lorem"; replacement = "<b>" + searchedWord +"</b>"; pattern = "(?!^)" + searchedWord; position = 2; // we will replace the 3rd occurence regExp = new RegExp(pattern, "gi"); newContent = originalContent.replace(regExp, function (occurence) { var toReturn; if (previousMatches === position) { // we're returning the replacement in this case toReturn = replacement; } else { // returning searchedWord is similar to not replacing this occurence toReturn = searchedWord; } // incrementing previousMatches previousMatches++; return toReturn; }); document.write("<br/><br/> After : <br/> " + newContent); 

<!DOCTYPE html>
<html>
<body>

<p>Click the button to set "blue", "house" and "car" to upper-case, in the paragraph below:</p>

<p id="demo">Mr Blue has a blue house and a blue car.</p>

<button onclick="myFunction()">Try it</button>

<script>
function myFunction() {
    var str = document.getElementById("demo").innerHTML; 
    var res = str.replace(/blue|house|car/gi, function (x) {
        return x.toUpperCase();
    });
    document.getElementById("demo").innerHTML = res;
}
</script>

</body>
</html>

I would just take the text, split it on spaces to get the exact words and then filter for the word and get the current index. As you have the indexes you are able to exchange a specific word at an index with whatever you desire and afterwards you can just join the array together again.

Something like this:

const text = "Lorem ipsum dolor sit amet, consectetur Lorem adipiscing elit. Vivamus ipsum Lorem eros, interdum ac cursus et, pulvinar Lorem at est. Integer nec rutrum ligula. In dignissim est a elit porttitor finibus.";

function exchangeWordInText(lookupWord, nr, text, newWord){

  const lookupTable = [];
  const wordArray = text.split(' ');

  wordArray.filter((word, index) => {
    if(word === lookupWord){
      lookupTable.push({ index: index , word: word});
    }
  });

  if(nr > 0 && nr <= lookupTable.length){
      const exchangeWord = lookupTable[nr-1];
      wordArray[exchangeWord.index] = newWord;
  }

  return wordArray.join(' ');
}

// This would exchange the first 'Lorem' in the text with 'CHANGED'
const result= exchangeWordInText('Lorem', 1, text, 'CHANGED');

RESULTS IN:

"CHANGED ipsum dolor sit amet, consectetur Lorem adipiscing elit. Vivamus ipsum Lorem eros, interdum ac cursus et, pulvinar Lorem at est. Integer nec rutrum ligula. In dignissim est a elit porttitor finibus."

It is not the most beautiful solution and it can for sure be enhanced (it's also case sensitive rightnow) but it might work for you! :)

JSBIN for checking: https://jsbin.com/lepuyifele/1/edit?js,console,output

kr, Georg

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM