JavaScript: sostituisce l'ultima occorrenza del testo in una stringa


97

Vedi il mio snippet di codice di seguito:

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')
     }
 }

Voglio sostituire l'ultima occorrenza della parola one con la parola finish nella stringa, quello che ho non funzionerà perché il metodo di sostituzione sostituirà solo la prima occorrenza di esso. Qualcuno sa come posso modificare quello snippet in modo che sostituisca solo l'ultima istanza di "uno"

Risposte:


115

Bene, se la stringa finisce davvero con lo schema, potresti farlo:

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

2
Buona idea. Prima è necessario eseguire l'escape di quella stringa (sebbene non con i suoi dati di esempio, concesso), il che non è banale. :-(
TJ Crowder,

@Ruth no prob! @TJ sì, in effetti è vero: Ruth se finisci con le "parole" che stai cercando che includono i caratteri speciali usati per le espressioni regolari, dovresti "sfuggire" a quelle, che come dice TJ è un po 'complicato (non impossibile però).
Pointy

cosa succede se si desidera sostituire l'ultima occorrenza di stringa1 all'interno di stringa2?
SuperUberDuper

1
@SuperUberDuper, beh, è ​​se vuoi abbinare qualcosa circondato da caratteri "/". Esistono due modi per creare un'istanza RegExp: il costruttore ( new RegExp(string)) e la sintassi inline ( /something/). Non hai bisogno dei caratteri "/" quando usi il costruttore RegExp.
Pointy

3
@TechLife dovresti applicare una trasformazione alla stringa per "proteggere" tutti i metacaratteri regex con ` - things like + , * , ? `, Parentesi, parentesi quadre, forse altre cose.
Pointy

35

È possibile utilizzare String#lastIndexOfper trovare l'ultima occorrenza della parola, quindi String#substringe la concatenazione per creare la stringa di sostituzione.

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

... o lungo queste linee.


1
Un altro esempio: var nameSplit = item.name.lastIndexOf (","); if (nameSplit! = -1) item.name = item.name.substr (0, nameSplit) + "e" + item.name.substr (nameSplit + 2);
Ben Gotow

22

So che è sciocco, ma questa mattina mi sento creativo:

'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"

Quindi, in realtà, tutto ciò che devi fare è aggiungere questa funzione al prototipo String:

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

Quindi eseguilo in questo modo: str = str.replaceLast('one', 'finish');

Una limitazione che dovresti sapere è che, poiché la funzione si divide per spazio, probabilmente non puoi trovare / sostituire nulla con uno spazio.

In realtà, ora che ci penso, potresti aggirare il problema dello "spazio" dividendo con un gettone vuoto.

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, per favore non usarlo nel codice di produzione ... O qualsiasi codice ... Una semplice regex sarà sufficiente.
Leggero

12

Non elegante come le risposte regex sopra, ma più facile da seguire per i meno esperti tra noi:

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);
}

Mi rendo conto che questo è probabilmente più lento e dispendioso degli esempi di regex, ma penso che potrebbe essere utile come illustrazione di come possono essere eseguite le manipolazioni delle stringhe. (Può anche essere condensato un po ', ma ancora una volta, volevo che ogni passaggio fosse chiaro.)


9

Ecco un metodo che utilizza solo la divisione e l'unione. È un po 'più leggibile, quindi ho pensato che valesse la pena condividerlo:

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

Funziona bene, tuttavia aggiungerà la sostituzione all'inizio della stringa se la stringa non include whataffatto. ad es'foo'.replaceLast('bar'); // 'barfoo'
pie6k

8

Ho pensato di rispondere qui poiché questo è emerso per primo nella mia ricerca su Google e non c'è risposta (al di fuori della risposta creativa di Matt :)) che sostituisce genericamente l'ultima occorrenza di una stringa di caratteri quando il testo da sostituire potrebbe non essere alla fine della stringa.

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

Una semplice risposta senza alcuna regex sarebbe:

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

2
Cosa succede se non ci sono eventi nella stringa originale?
tomazahlin

La risposta più accettata restituisce lo stesso risultato del mio! Ecco il risultato del test:
Tim Long

Il mio:> s = s.substr (0, s.lastIndexOf ('d')) + 'finish' => 'finish' ... Il loro:> s = s.replace (new RegExp ('d' + '$ '),' finish ') =>' finish '
Tim Long

2

Non potresti semplicemente invertire la stringa e sostituire solo la prima occorrenza del modello di ricerca invertito? Sto pensando . . .

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

Se la velocità è importante, usa questo:

/**
 * 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("");
}

È più veloce rispetto all'utilizzo di RegExp.


1

Codice vecchio stile e grande ma efficiente possibile:

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

Una soluzione semplice sarebbe usare il metodo della sottostringa. Poiché la stringa termina con l'elemento della lista, possiamo usare string.length e calcolare l'indice di fine per la sottostringa senza usare il lastIndexOfmetodo

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


1

Non mi è piaciuta nessuna delle risposte di cui sopra e ho trovato la sotto

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);
}

Utilizzo:

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

Spero che aiuti!


0

Suggerirei di utilizzare il pacchetto replace-last npm.

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>

Funziona per le sostituzioni di stringhe e regex.


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

6
Normalmente, una spiegazione è generalmente richiesta in aggiunta alla risposta
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.