JavaScript: замінити останнє входження тексту в рядок


97

Дивіться мій фрагмент коду нижче:

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

Я хочу замінити останнє виникнення слова одне словом закінчення в рядку, те, що у мене не вийде, оскільки метод заміни замінить лише перше його виникнення. Хтось знає, як я можу змінити цей фрагмент, щоб він замінив лише останній екземпляр "one"

Відповіді:


115

Ну, якщо рядок дійсно закінчується шаблоном, ви можете зробити це:

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

2
Гарна ідея. Потрібно спершу вийти з тієї рядки (хоча не з її зразковими даними, наданими), що нетривіально. :-(
— TJ Crowder,

@Ruth no prob! @TJ так, насправді це правда: Рут, якщо в результаті ви отримаєте "слова", які ви шукаєте, які включають спеціальні символи, що використовуються для регулярних виразів, вам потрібно буде "уникнути" тих, що, як каже TJ, трохи складно (хоча не неможливо).
— Пойнті

що робити, якщо ви хочете замінити останню появу string1 всередині string2?
— SuperUberDuper

1
@SuperUberDuper добре, якщо ви хочете, щоб щось відповідало оточенню символів "/". Існує два способи створення екземпляра RegExp: конструктор ( new RegExp(string)) та вбудований синтаксис ( /something/). Для використання конструктора RegExp вам не потрібні символи "/".
— Пойнті

3
@TechLife вам довелося б застосувати перетворення до рядка, щоб "захистити" всі метасимволи регулярних виразів знаками ` - things like + , * , ? `, Дужками, квадратними дужками, можливо, іншими речами.
— Пойнті

35

Ви можете використовувати, String#lastIndexOfщоб знайти останнє входження слова, а потім String#substringі конкатенацію, щоб побудувати рядок заміни.

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

... або по цих лініях.


1
Інший приклад: var nameSplit = item.name.lastIndexOf (","); if (nameSplit! = -1) item.name = item.name.substr (0, nameSplit) + "і" + item.name.substr (nameSplit + 2);
— Бен Готу

22

Я знаю, що це нерозумно, але я почуваюся творчим сьогодні вранці:

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

Отже, все, що вам потрібно зробити, - це додати цю функцію до прототипу String:

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

Потім запустіть його так: str = str.replaceLast('one', 'finish');

Одне обмеження, яке ви повинні знати, полягає в тому, що, оскільки функція розділена на пробіл, ви, мабуть, не можете нічого знайти / замінити пробілом.

Насправді, тепер, коли я думаю про це, ви могли обійти проблему «простору», розділивши порожній маркер.

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, будь ласка, не використовуйте це у виробничому коді ... Або будь-який код ... Достатнього простого регулярного вираження.
— Незначно

12

Не настільки витончено, як відповідає регекс вище, але легше наслідувати для не дуже кмітливих серед нас:

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

Я усвідомлюю, що це, мабуть, повільніше і марніше, ніж приклади регулярних виразів, але я думаю, що це може бути корисним як ілюстрацію того, як можна робити строкові маніпуляції. (Його також можна трохи ущільнити, але знову ж таки, я хотів, щоб кожен крок був чітким.)


9

Ось метод, який використовує лише розщеплення та з'єднання. Він трохи читабельніший, тому вважав, що варто поділитися ним:

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

Це добре працює, однак додасть заміну до початку рядка, якщо рядок взагалі не включає what. наприклад'foo'.replaceLast('bar'); // 'barfoo'
— pie6k

8

Думав, я б відповів тут, оскільки це з'явилося першим у моєму пошуку Google, і немає відповіді (за межами творчої відповіді Метта :)), яка б загалом заміняла останнє виникнення рядка символів, коли текст для заміни може бути не в кінці рядка.

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

Проста відповідь без будь-якого регулярного виразу буде такою:

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

2
Що робити, якщо у вихідному рядку немає випадків?
— tomazahlin

Найприйнята відповідь повертає той самий результат, що і мій! Ось результат тесту:
— Тім Лонг

Шахта:> s = s.substr (0, s.lastIndexOf ('d')) + 'закінчити' => 'закінчити' ... Їх:> s = s.replace (новий RegExp ('d' + '$ '),' фініш ') =>' фініш '
— Тім Лонг

2

Не могли ви просто змінити рядок і замінити лише перше виникнення зворотного шаблону пошуку? Я маю на увазі . . .

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

Якщо швидкість важлива, використовуйте це:

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

Це швидше, ніж використання RegExp.


1

Старомодний і великий код, але максимально ефективний:

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

Простим рішенням було б використання методу підрядків. Оскільки рядок закінчується елементом списку, ми можемо використовувати string.length та обчислити кінцевий індекс для підрядки без використання lastIndexOfметоду

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


1

Мені не сподобалася жодна з відповідей, наведених вище, і я придумав нижче

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

Використання:

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

Сподіваюся, що це допомагає!


0

Я б запропонував використовувати пакет 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>

Це працює для заміни рядків і регулярних виразів.


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

6
Зазвичай, окрім відповіді, як правило, потрібне пояснення
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.