web-dev-qa-db-fra.com

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

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 de celle-ci par Word avec la finition Word dans la chaîne. Ce que je possède ne fonctionnera pas car la méthode de remplacement ne remplacera que la première occurrence. Est-ce que quelqu'un sait comment je peux modifier cet extrait pour qu'il ne remplace que la dernière instance de 'un'

63
Ruth

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

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

Vous pouvez utiliser String#lastIndexOf pour rechercher la dernière occurrence du mot, puis String#substring et la concaténation pour générer 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 le long de ces lignes.

34
T.J. Crowder

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, tout ce dont vous avez besoin 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 probablement ne pouvez rien trouver/remplacer par un espace.

En fait, maintenant que j'y pense, vous pouvez contourner le problème de l'espace en scindant 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');
16
Matt

Pas aussi élégant que les réponses regex ci-dessus, mais plus facile à suivre pour ceux qui ne sont pas 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 réalise 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 des manipulations de chaîne peuvent être effectuées. (Cela peut aussi être un peu condensé, mais encore une fois, je voulais que chaque étape soit claire.) 

10
WilsonCPU

Je pensais que je devrais répondre ici car cela est apparu en premier dans ma recherche Google et qu'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 risque de ne pas être à 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(',', ';'));
8
Irving

Voici une méthode qui utilise uniquement le fractionnement et la jointure. C'est un peu plus lisible alors j'ai pensé que ça 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;
    };
4
mr.freeze

Une réponse simple sans regex serait:

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

Ne pourriez-vous pas simplement inverser la chaîne et ne remplacer que 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('');
     }
 }
2
Fusty

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.

1
Pascut

Vieux code et gros code mais 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);
    }
}
1
dario nascimento

Je suggérerais d’utiliser le paquet 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.

0
danday74

Je n'ai pas aimé aucune des réponses ci-dessus et est venu avec le 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!

0
Pepijn Olivier