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.