JavaScript: remplace la dernière occurrence de texte dans une chaîne

97

Voir mon extrait de code ci-dessous:

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

Je veux remplacer la dernière occurrence du mot un par le mot finish dans la chaîne, ce que j'ai ne fonctionnera pas car la méthode replace ne remplacera que la première occurrence de celui-ci. Quelqu'un sait-il comment je peux modifier cet extrait de code afin qu'il ne remplace que la dernière instance de "un"

Ruth
la source

Réponses:

115

Eh bien, si la chaîne se termine vraiment par le motif, vous pouvez le faire:

str = str.replace(new RegExp(list[i] + '$'), 'finish');
Pointu
la source
2
Bonne idée. Besoin d'échapper d'abord à cette chaîne (mais pas avec ses exemples de données), ce qui n'est pas trivial. :-(
TJ Crowder
@Ruth pas de problème! @TJ oui, en effet c'est vrai: Ruth si vous vous retrouvez avec des "mots" que vous recherchez qui incluent les caractères spéciaux utilisés pour les expressions régulières, vous devrez les "échapper", ce qui, comme le dit TJ, est un peu délicat (pas impossible cependant).
Pointy
que faire si vous voulez remplacer la dernière occurrence de string1 à l'intérieur de string2?
SuperUberDuper
1
@SuperUberDuper c'est bien si vous voulez faire correspondre quelque chose entouré de caractères "/". Il existe deux façons de créer une instance RegExp: le constructeur ( new RegExp(string)) et la syntaxe en ligne ( /something/). Vous n'avez pas besoin des caractères "/" lorsque vous utilisez le constructeur RegExp.
Pointy
3
@TechLife vous devriez appliquer une transformation à la chaîne pour "protéger" tous les métacaractères regex avec ` - things like + , * , ? `, Des parenthèses, des crochets, peut-être d'autres choses.
Pointy
35

Vous pouvez utiliser String#lastIndexOfpour rechercher la dernière occurrence du mot, puis une String#substringconcaténation pour créer la chaîne de remplacement.

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

... ou dans ce sens.

TJ Crowder
la source
1
Un autre exemple: var nameSplit = item.name.lastIndexOf (","); if (nameSplit! = -1) item.name = item.name.substr (0, nameSplit) + "et" + item.name.substr (nameSplit + 2);
Ben Gotow
22

Je sais que c'est idiot, mais je me sens créatif ce matin:

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

Donc vraiment, tout ce que vous avez à faire est d'ajouter cette fonction au prototype String:

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

Puis lancez-le comme ceci: str = str.replaceLast('one', 'finish');

Une limitation que vous devez savoir est que, puisque la fonction est divisée par espace, vous ne pouvez probablement rien trouver / remplacer par un espace.

En fait, maintenant que j'y pense, vous pouvez contourner le problème de «l'espace» en divisant avec un jeton vide.

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');
Mat
la source
11
@Alexander Euh, veuillez ne pas utiliser ça dans le code de production ... Ou n'importe quel code ... Une simple regex suffira.
Légère
12

Pas aussi élégant que les réponses regex ci-dessus, mais plus facile à suivre pour les moins avertis parmi nous:

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

Je me rends compte que c'est probablement plus lent et plus coûteux que les exemples de regex, mais je pense que cela pourrait être utile pour illustrer comment les manipulations de chaînes peuvent être effectuées. (Cela peut aussi être un peu condensé, mais encore une fois, je voulais que chaque étape soit claire.)

WilsonCPU
la source
9

Voici une méthode qui utilise uniquement le fractionnement et la jointure. C'est un peu plus lisible, alors j'ai pensé que cela valait la peine d'être partagé:

    String.prototype.replaceLast = function (what, replacement) {
        var pcs = this.split(what);
        var lastPc = pcs.pop();
        return pcs.join(what) + replacement + lastPc;
    };
mr.freeze
la source
Cela fonctionne bien, mais cela ajoutera un remplacement au début de la chaîne si la chaîne ne comprend pas whatdu tout. par exemple'foo'.replaceLast('bar'); // 'barfoo'
pie6k
8

Je pensais que je répondrais ici car cela est apparu en premier dans ma recherche Google et il n'y a pas de réponse (en dehors de la réponse créative de Matt :)) qui remplace de manière générique la dernière occurrence d'une chaîne de caractères lorsque le texte à remplacer n'est peut-être pas à la fin de la chaîne.

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(',', ';'));
Irving
la source
4

Une réponse simple sans aucune regex serait:

str = str.substr(0, str.lastIndexOf(list[i])) + 'finish'
Tim Long
la source
2
Que faire s'il n'y a pas d'occurrence dans la chaîne d'origine?
tomazahlin
La réponse la plus acceptée renvoie le même résultat que le mien! Voici le résultat du test:
Tim Long
Mine:> s = s.substr (0, s.lastIndexOf ('d')) + 'finish' => 'finish' ... Theirs:> s = s.replace (new RegExp ('d' + '$ '),' finish ') =>' finish '
Tim Long
2

Ne pourriez-vous pas simplement inverser la chaîne et remplacer uniquement la première occurrence du modèle de recherche inversé? Je pense . . .

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('');
     }
 }
De renfermé
la source
2

Si la vitesse est importante, utilisez ceci:

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

C'est plus rapide que d'utiliser RegExp.

Pascut
la source
1

Ancien et gros code mais aussi efficace que possible:

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);
    }
}
dario nascimento
la source
1

Une solution simple serait d'utiliser la méthode des sous-chaînes. Puisque la chaîne se termine par un élément de liste, nous pouvons utiliser string.length et calculer l'index de fin pour la sous-chaîne sans utiliser la lastIndexOfméthode

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

Hexer338
la source
1

Je n'ai aimé aucune des réponses ci-dessus et j'ai trouvé les réponses ci-dessous

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

Usage:

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

J'espère que cela pourra aider!

Pepijn Olivier
la source
0

Je suggérerais d'utiliser le package 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>

Cela fonctionne pour les remplacements de chaînes et de regex.

danday74
la source
-1
str = (str + '?').replace(list[i] + '?', 'finish');
zaxvox
la source
6
Normalement, une explication est généralement souhaitée en plus de la réponse