JavaScript: Ersetzt das letzte Vorkommen von Text in einer Zeichenfolge


96

Siehe mein Code-Snippet unten:

var list = ['one', 'two', 'three', 'four'];
var str = 'one two, one three, one four, one';
for ( var i = 0; i < list.length; i++)
{
     if (str.endsWith(list[i])
     {
         str = str.replace(list[i], 'finish')
     }
 }

Ich möchte das letzte Vorkommen des Wortes eins durch das Ende des Wortes in der Zeichenfolge ersetzen. Was ich habe, funktioniert nicht, da die Ersetzungsmethode nur das erste Vorkommen des Wortes ersetzt. Weiß jemand, wie ich dieses Snippet so ändern kann, dass es nur die letzte Instanz von 'one' ersetzt?

Antworten:


114

Wenn die Zeichenfolge wirklich mit dem Muster endet, können Sie Folgendes tun:

str = str.replace(new RegExp(list[i] + '$'), 'finish');

2
Gute Idee. Diese Zeichenfolge muss zuerst maskiert werden (allerdings nicht mit ihren gewährten Beispieldaten), was nicht trivial ist. :-(
TJ Crowder

@ Ruth kein Problem! @TJ ja, in der Tat, das stimmt: Ruth, wenn Sie am Ende "Wörter" haben, nach denen Sie suchen, einschließlich der Sonderzeichen, die für reguläre Ausdrücke verwendet werden, müssten Sie diesen "entkommen", was, wie TJ sagt, etwas schwierig ist (aber nicht unmöglich).
Pointy

Was ist, wenn Sie das letzte Vorkommen von string1 in string2 ersetzen möchten?
SuperUberDuper

1
@ SuperUberDuper gut, wenn Sie etwas finden möchten, das von "/" Zeichen umgeben ist. Es gibt zwei Möglichkeiten, eine RegExp-Instanz zu erstellen: den Konstruktor ( new RegExp(string)) und die Inline-Syntax ( /something/). Sie benötigen die Zeichen "/" nicht, wenn Sie den RegExp-Konstruktor verwenden.
Pointy

3
@TechLife Sie müssten eine Transformation auf die Zeichenfolge anwenden, um alle Regex-Metazeichen mit ` - things like + , * , ? `, Klammern, eckigen Klammern und möglicherweise anderen Dingen zu" schützen " .
Pointy

35

Sie können verwenden String#lastIndexOf, um das letzte Vorkommen des Wortes zu finden, und dann String#substringund die Verkettung, um die Ersatzzeichenfolge zu erstellen.

n = str.lastIndexOf(list[i]);
if (n >= 0 && n + list[i].length >= str.length) {
    str = str.substring(0, n) + "finish";
}

... oder in diese Richtung.


1
Ein weiteres Beispiel: var nameSplit = item.name.lastIndexOf (","); if (nameSplit! = -1) item.name = item.name.substr (0, nameSplit) + "und" + item.name.substr (nameSplit + 2);
Ben Gotow

21

Ich weiß, dass das albern ist, aber ich fühle mich heute Morgen kreativ:

'one two, one three, one four, one'
.split(' ') // array: ["one", "two,", "one", "three,", "one", "four,", "one"]
.reverse() // array: ["one", "four,", "one", "three,", "one", "two,", "one"]
.join(' ') // string: "one four, one three, one two, one"
.replace(/one/, 'finish') // string: "finish four, one three, one two, one"
.split(' ') // array: ["finish", "four,", "one", "three,", "one", "two,", "one"]
.reverse() // array: ["one", "two,", "one", "three,", "one", "four,", "finish"]
.join(' '); // final string: "one two, one three, one four, finish"

Alles, was Sie tun müssen, ist, diese Funktion zum String-Prototyp hinzuzufügen:

String.prototype.replaceLast = function (what, replacement) {
    return this.split(' ').reverse().join(' ').replace(new RegExp(what), replacement).split(' ').reverse().join(' ');
};

Dann führen Sie es so aus: str = str.replaceLast('one', 'finish');

Eine Einschränkung, die Sie kennen sollten, ist, dass Sie wahrscheinlich nichts durch ein Leerzeichen finden / ersetzen können , da die Funktion nach Leerzeichen aufgeteilt wird .

Jetzt, wo ich darüber nachdenke, könnten Sie das Platzproblem umgehen, indem Sie es mit einem leeren Token aufteilen.

String.prototype.reverse = function () {
    return this.split('').reverse().join('');
};

String.prototype.replaceLast = function (what, replacement) {
    return this.reverse().replace(new RegExp(what.reverse()), replacement.reverse()).reverse();
};

str = str.replaceLast('one', 'finish');

11
@Alexander Uhhh, bitte verwenden Sie dies nicht im Produktionscode ... oder in irgendeinem Code ... Eine einfache Regex wird ausreichen.
Leicht

12

Nicht so elegant wie die Regex-Antworten oben, aber für die nicht so versierten unter uns leichter zu befolgen:

function removeLastInstance(badtext, str) {
    var charpos = str.lastIndexOf(badtext);
    if (charpos<0) return str;
    ptone = str.substring(0,charpos);
    pttwo = str.substring(charpos+(badtext.length));
    return (ptone+pttwo);
}

Mir ist klar, dass dies wahrscheinlich langsamer und verschwenderischer ist als die Regex-Beispiele, aber ich denke, es könnte hilfreich sein, um zu veranschaulichen, wie String-Manipulationen durchgeführt werden können. (Es kann auch etwas verdichtet werden, aber ich wollte, dass jeder Schritt klar ist.)


9

Hier ist eine Methode, die nur das Teilen und Verbinden verwendet. Es ist etwas lesbarer und dachte, es lohnt sich zu teilen:

    String.prototype.replaceLast = function (what, replacement) {
        var pcs = this.split(what);
        var lastPc = pcs.pop();
        return pcs.join(what) + replacement + lastPc;
    };

Es funktioniert gut, fügt jedoch dem Anfang der Zeichenfolge eine Ersetzung hinzu, wenn die Zeichenfolge überhaupt nicht enthalten ist what. zB'foo'.replaceLast('bar'); // 'barfoo'
pie6k

8

Ich dachte, ich würde hier antworten, da dies zuerst in meiner Google-Suche auftauchte und es keine Antwort (außerhalb von Matts kreativer Antwort :) gibt, die generisch das letzte Auftreten einer Zeichenfolge ersetzt, wenn der zu ersetzende Text möglicherweise nicht am Ende steht der Zeichenfolge.

if (!String.prototype.replaceLast) {
    String.prototype.replaceLast = function(find, replace) {
        var index = this.lastIndexOf(find);

        if (index >= 0) {
            return this.substring(0, index) + replace + this.substring(index + find.length);
        }

        return this.toString();
    };
}

var str = 'one two, one three, one four, one';

// outputs: one two, one three, one four, finish
console.log(str.replaceLast('one', 'finish'));

// outputs: one two, one three, one four; one
console.log(str.replaceLast(',', ';'));

4

Eine einfache Antwort ohne Regex wäre:

str = str.substr(0, str.lastIndexOf(list[i])) + 'finish'

2
Was ist, wenn die ursprüngliche Zeichenfolge nicht vorkommt?
Tomazahlin

Die am meisten akzeptierte Antwort liefert das gleiche Ergebnis wie meine! Hier ist das Testergebnis:
Tim Long

Meins:> s = s.substr (0, s.lastIndexOf ('d')) + 'finish' => 'finish' ... Ihre:> s = s.replace (neues RegExp ('d' + '$) '),' finish ') =>' finish '
Tim Long

2

Könnten Sie nicht einfach die Zeichenfolge umkehren und nur das erste Auftreten des umgekehrten Suchmusters ersetzen? Ich denke . . .

var list = ['one', 'two', 'three', 'four'];
var str = 'one two, one three, one four, one';
for ( var i = 0; i < list.length; i++)
{
     if (str.endsWith(list[i])
     {
         var reversedHaystack = str.split('').reverse().join('');
         var reversedNeedle = list[i].split('').reverse().join('');

         reversedHaystack = reversedHaystack.replace(reversedNeedle, 'hsinif');
         str = reversedHaystack.split('').reverse().join('');
     }
 }

2

Wenn Geschwindigkeit wichtig ist, verwenden Sie Folgendes:

/**
 * Replace last occurrence of a string with another string
 * x - the initial string
 * y - string to replace
 * z - string that will replace
 */
function replaceLast(x, y, z){
    var a = x.split("");
    var length = y.length;
    if(x.lastIndexOf(y) != -1) {
        for(var i = x.lastIndexOf(y); i < x.lastIndexOf(y) + length; i++) {
            if(i == x.lastIndexOf(y)) {
                a[i] = z;
            }
            else {
                delete a[i];
            }
        }
    }

    return a.join("");
}

Es ist schneller als RegExp.


1

Altmodischer und großer Code, aber so effizient wie möglich:

function replaceLast(origin,text){
    textLenght = text.length;
    originLen = origin.length
    if(textLenght == 0)
        return origin;

    start = originLen-textLenght;
    if(start < 0){
        return origin;
    }
    if(start == 0){
        return "";
    }
    for(i = start; i >= 0; i--){
        k = 0;
        while(origin[i+k] == text[k]){
            k++
            if(k == textLenght)
                break;
        }
        if(k == textLenght)
            break;
    }
    //not founded
    if(k != textLenght)
        return origin;

    //founded and i starts on correct and i+k is the first char after
    end = origin.substring(i+k,originLen);
    if(i == 0)
        return end;
    else{
        start = origin.substring(0,i) 
        return (start + end);
    }
}

1

Eine einfache Lösung wäre die Verwendung der Teilzeichenfolge. Da der String mit dem Listenelement endet, können wir string.length verwenden und den Endindex für den Teilstring ohne Verwendung der lastIndexOfMethode berechnen

str = str.substring(0, str.length - list[i].length) + "finish"


1

Ich mochte keine der obigen Antworten und fand die folgenden

function replaceLastOccurrenceInString(input, find, replaceWith) {
    if (!input || !find || !replaceWith || !input.length || !find.length || !replaceWith.length) {
        // returns input on invalid arguments
        return input;
    }

    const lastIndex = input.lastIndexOf(find);
    if (lastIndex < 0) {
        return input;
    }

    return input.substr(0, lastIndex) + replaceWith + input.substr(lastIndex + find.length);
}

Verwendung:

const input = 'ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen twenty';
const find = 'teen';
const replaceWith = 'teenhundred';

const output = replaceLastOccurrenceInString(input, find, replaceWith);
console.log(output);

// output: ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteenhundred twenty

Hoffentlich hilft das!


0

Ich würde vorschlagen, das Paket replace-last npm zu verwenden.

var str = 'one two, one three, one four, one';
var result = replaceLast(str, 'one', 'finish');
console.log(result);
<script src="https://unpkg.com/replace-last@latest/replaceLast.js"></script>

Dies funktioniert für das Ersetzen von Zeichenfolgen und Regex.


-1
str = (str + '?').replace(list[i] + '?', 'finish');

6
Normalerweise ist eine Erklärung im Allgemeinen zusätzlich zu der Antwort
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.