Як замінити символ у певному індексі в JavaScript?


544

Скажімо, у мене є рядок, скажімо, Hello worldі мені потрібно замінити char на індекс 3. Як можна замінити char, вказавши індекс?

var str = "hello world";

Мені потрібно щось на кшталт

str.replaceAt(0,"h");

131
Що дивно, що str[0] = 'x'це, здається , не призводить до помилок, але не має бажаного ефекту!
Майкл

6
@Michael з цим ви отримаєте індекс у 0, встановите його на 'x', це твердження саме по собі поверне нове значення; 'х'. але все це не змінює оригінал, тому його ідеально дійсне, просто не те, що ви очікували. це не посилання
Пол Шельтема

8
@Michael це робить, якщо "use strict"активовано: Uncaught TypeError: Cannot assign to read only property '0' of string 'hello world'(принаймні, у веб-браузерах)
Jan Turoň

1
Рядки Javascript незмінні, їх неможливо змінити "на місці", тому ви не можете змінити жодного символу. насправді кожне виникнення однієї струни - це ОДИН об'єкт.
Martijn Scheffer

добре, дивіться відповідь: D
Шеффер

Відповіді:


611

У JavaScript рядки незмінні , а значить, найкраще, що ви можете зробити, це створити нову рядок зі зміненим вмістом і призначити змінну, щоб вказати на неї.

Вам потрібно буде визначити replaceAt()функцію самостійно:

String.prototype.replaceAt = function(index, replacement) {
    return this.substr(0, index) + replacement + this.substr(index + replacement.length);
}

І використовуйте його так:

var hello = "Hello World";
alert(hello.replaceAt(2, "!!")); // Should display He!!o World

101
Зауважте, що, як правило, розширювати базові класи JavaScript не годиться. Замість цього використовуйте звичайну функцію утиліти.
Атес Горал

86
Я повинен запитати, чому? Для цієї мети існує підтримка прототипу.
Cem Kalyoncu

30
@CemKalyoncu: Це може призвести до поганого відтворення коду з іншими бібліотеками. Але мене ніколи цього не кусали.
alex

6
@alex: ви маєте рацію з цим, вам дійсно потрібно перевірити, чи існує ця функція, перш ніж виконувати її. Але все ж, навіть якщо це зробити, він повинен зробити точно таку ж операцію.
Cem Kalyoncu

80
Я не згоден, навіть якщо ви виявите, чи існує функція, пізніша бібліотека все ще може використовувати / створювати подібну функцію - 2 погані ідеї роблять гіршу. Взагалі, просто ніколи не грайте з кодом інших народів (включайте основну систему). Створіть нову функцію.
мученик

93

У replaceAtJavaScript немає функції. Ви можете використовувати наступний код для заміни будь-якого символу в будь-якому рядку у визначеному положенні:

function rep() {
    var str = 'Hello World';
    str = setCharAt(str,4,'a');
    alert(str);
}

function setCharAt(str,index,chr) {
    if(index > str.length-1) return str;
    return str.substr(0,index) + chr + str.substr(index+1);
}
<button onclick="rep();">click</button>



Я вважаю за краще це рішення, оскільки ви можете замінити його одним символом. Рішення з найбільшою кількістю оновлень не працює, якщо ви хочете замінити лише один символ. Він працює лише при заміні двох символів! Крім того, підстрочку слід замінити на підрядку, як згадується у багатьох інших коментарях.
gignu

74

Ви не можете. Візьміть символи до позиції та після неї та контур в нову рядок:

var s = "Hello world";
var index = 3;
s = s.substring(0, index) + 'x' + s.substring(index + 1);

Насправді, так, ви можете, але це було б надлишком. Ви можете встановити регулярний вираз, щоб пропустити перший індекс - 1 символ і відповідати символу в індексі. Ви можете використовувати String.replace з цим регулярним виразом, щоб зробити пряму заміну на місці. Але це надмірність. Тож на практиці не можна. Але теоретично можна.
Атес Горал

12
@Ates: Функція заміни не робить заміни на місці, вона створює нову рядок і повертає її.
Гуффа

2
MDN пропонує використовувати String.prototype.substringбільше String.prototype.substr.
Mikemike

33

Тут є багато відповідей, і всі вони базуються на двох методах:

  • METHOD1: розділіть рядок за допомогою двох підрядків і вставте символ між ними
  • METHOD2: перетворіть рядок у масив символів, замініть один елемент масиву та приєднайтеся до нього

Особисто я би використав ці два методи в різних випадках. Дозволь пояснити.

@FabioPhms: Ваш метод був таким, який я спочатку використовував, і я побоювався, що це погано в рядку з великою кількістю символів. Однак питання в чому багато персонажів? Я перевірив його на 10 абзацах "lorem ipsum", і це зайняло кілька мілісекунд. Тоді я тестував його на 10 разів більшу струну - різниці справді не було. Гм.

@vsync, @Cory Mawhorter: Ваші коментарі однозначні; однак, знову ж таки, що таке велика струна? Я погоджуюся, що для 32 ... 100kb продуктивність повинна бути кращою, і для цієї операції заміни символів слід використовувати варіант substring.

Але що буде, якщо мені доведеться зробити досить багато замін?

Мені потрібно було виконати власні тести, щоб довести, що швидше в такому випадку. Скажімо, у нас є алгоритм, який маніпулює відносно коротким рядком, який складається з 1000 символів. Ми очікуємо, що в середньому кожен символ у цьому рядку буде замінено ~ 100 разів. Отже, код для тестування чогось такого:

var str = "... {A LARGE STRING HERE} ...";

for(var i=0; i<100000; i++)
{
  var n = '' + Math.floor(Math.random() * 10);
  var p = Math.floor(Math.random() * 1000);
  // replace character *n* on position *p*
}

Я створив для цього загадку, і вона тут . Є два тести, TEST1 (підрядка) та TEST2 (перетворення масиву).

Результати:

  • TEST1: 195ms
  • TEST2: 6 мс

Здається, що перетворення масиву б'є підрядку на 2 порядки! Отже - що за чорт тут сталося ???

Що насправді відбувається, так це те, що всі операції в TEST2 виконуються на самому масиві, використовуючи вираз призначення, як strarr2[p] = n. Присвоєння дійсно швидко порівняно з підрядкою на великій струні, і зрозуміло, що вона виграє.

Отже, справа у виборі правильного інструменту для роботи. Знову.


7
Перенесли свій тест на jsperf, із значно різними результатами: jsperf.com/string-replace-character . Вся справа у виборі правильного інструменту для роботи;)
colllin

1
@colllin: Я абсолютно згоден. Тому я майже буквально клонував тести від jsfiddle до jsperf. Однак, окрім використання правильного інструменту, ви повинні це робити правильно. Зверніть увагу на проблему, ми говоримо про те, що буде, якщо нам доведеться зробити декілька замін в алгоритмі. Конкретний приклад включає 10000 замін. Це означає, що один тест повинен включати 10000 замін. Отже, використовуючи jsperf, я отримав цілком послідовні результати, див. jsperf.com/… .
OzrenTkalcecKrznaric

1
саме тому мені не подобається JavaScript. такі часті, інтуїтивні, очевидні випадки використання не передбачені. Ви можете подумати, що письменникам javascript станеться, що люди можуть захотіти підміняти символи в рядках, і що вони вирішать використовувати зручні методи на мові.
ahnbizcad

1
@colllin зробив щось інше для OzrenTkalcecKrznaric. OzrenTkalcecKrznaric зробив припущення, що рядок є статичним і розкол можна оптимізувати перед запуском. Що не обов’язково правда. якщо вам доводиться розділяти під час кожного запуску, тоді підхід до підрядки на малих рядках приблизно вдвічі-в 4 рази швидше, і лише швидше, чим більший стає рядок. На мою думку, це робить підрядку кращою реалізацією для більшості випадків використання, забороняючи велику кількість змін у великій статичній рядку.
WiR3D

3
Проблема полягає в тому, що на test2 розділення та з'єднання знаходяться поза циклом for, де ви порівнюєте одну заміну char з кратною (несправедливою), перший метод швидший для однократних заміщень, а другий - краще для ланцюгових замін один за іншим
Θεόφιλος Μουρατίδης

32

Робота з векторами, як правило, найефективніша для контакту з String

Я пропоную наступну функцію:

String.prototype.replaceAt=function(index, char) {
    var a = this.split("");
    a[index] = char;
    return a.join("");
}

Запустіть цей фрагмент:

String.prototype.replaceAt=function(index, char) {
    var a = this.split("");
    a[index] = char;
    return a.join("");
}

var str = "hello world";
str = str.replaceAt(3, "#");

document.write(str);


9
це погано для великих рядків, не потрібно створювати масив з шаленою кількістю комірок, якщо ви можете просто використовувати substr, щоб вирізати його ...
vsync

1
Корисно, якщо вам потрібно змінити багато символів, і це просто.
Sheepy

6
Я створив тест на jsperf: jsperf.com/replace-character-by-index - Substr набагато швидший і стабільний від 32 байт до 100 кбіт. Я підтримав це і наскільки мені боляче сказати, хоча це відчуває себе приємніше, substr - кращий вибір для рядків будь-якого розміру.
Cory Mawhorter

Це зовсім неоптимально.
Радько Дінев

Це хороше швидке рішення, якщо ви знаєте, що збираєтесь використовувати невеликі рядки. Якщо ви добре з розширенням прототипів, ви могли б досягти подібної поведінки у 2 рядки:Array.prototype.replace = function(index, val) { this[index] = val; return this; }; 'word'.split('').replace(1, '0').join(''); // returns 'w0rd'
Майкл Плаут

29

У Javascript рядки незмінні, тому вам доведеться робити щось подібне

var x = "Hello world"
x = x.substring(0, i) + 'h' + x.substring(i+1);

Щоб замінити символ у x на i на 'h'


28
str = str.split('');
str[3] = 'h';
str = str.join('');

4
Чіткий і простий, але наразі не працюватиме з емоджими (такими як when) при використанні splitтаjoin
Зелений

1
З es6 я думаю, що Array.from (str) зараз може бути кращим варіантом, але я прийшов сюди з цими знаннями і навчився чогось настільки ж стислого від вас, тож дякую!
Девід Камер

7

Однолінійний за допомогою String.replace з зворотним дзвоном (відсутність підтримки емоцій):

// 0 - index to replace, 'f' - replacement string
'dog'.replace(/./g, (c, i) => i == 0? 'f': c)
// "fog"

Пояснили:

//String.replace will call the callback on each pattern match
//in this case - each character
'dog'.replace(/./g, function (character, index) {
   if (index == 0) //we want to replace the first character
     return 'f'
   return character //leaving other characters the same
})

6

Цей метод хороший для рядків невеликої довжини, але може бути повільним для більшого тексту.

var x = "White Dog";
var arr = x.split(""); // ["W", "h", "i", "t", "e", " ", "D", "o", "g"]
arr.splice(6, 1, 'F');
var result = arr.join(""); // "White Fog"

/* 
  Here 6 is starting index and 1 is no. of array elements to remove and 
  final argument 'F' is the new character to be inserted. 
*/

3

@CemKalyoncu: Дякую за чудову відповідь!

Я також трохи адаптував його, щоб зробити його більше схожим на метод Array.splice (і взяв до уваги @Ates):

spliceString=function(string, index, numToDelete, char) {
      return string.substr(0, index) + char + string.substr(index+numToDelete);
   }

var myString="hello world!";
spliceString(myString,myString.lastIndexOf('l'),2,'mhole'); // "hello wormhole!"

Не впевнений, чому це було проголосовано. Це законна відповідь. Він працює, як зазначено. І це відповідає Array.spliceфункціональності типу.
Скримотій

3

Це працює аналогічно Array.splice:

String.prototype.splice = function (i, j, str) {
    return this.substr(0, i) + str + this.substr(j, this.length);
};


2

Якщо ви хочете замінити символи в рядку, вам слід створити змінні рядки. По суті це масиви символів. Ви можете створити завод:

  function MutableString(str) {
    var result = str.split("");
    result.toString = function() {
      return this.join("");
    }
    return result;
  }

Тоді ви можете отримати доступ до символів і весь масив перетворюється на рядок, коли він використовується як рядок:

  var x = MutableString("Hello");
  x[0] = "B"; // yes, we can alter the character
  x.push("!"); // good performance: no new string is created
  var y = "Hi, "+x; // converted to string: "Hi, Bello!"

2

Узагальнюючи відповідь Афанасія Куракіна, ми маємо:

function replaceAt(str, index, ch) {
    return str.replace(/./g, (c, i) => i == index ? ch : c)
}

let str = 'Hello World'
str = replaceAt(str, 1, 'u')
console.log(str) // Hullo World

Розкриємо і пояснимо як регулярний вираз, так і функцію заміщення:

function replaceAt(str, index, newChar) {
    function replacer(origChar, strIndex) {
        if (strIndex === index)
            return newChar
        else
            return origChar
    }
    return str.replace(/./g, replacer)
}

let str = 'Hello World'
str = replaceAt(str, 1, 'u')
console.log(str) // Hullo World

Регулярний вираз .відповідає точно одному символу. Це gробить його відповідним кожному символу в циклі for. replacerФункція викликається з урахуванням як оригінальний характер і індекс , де цей символ в рядку. Ми робимо просту ifзаяву, щоб визначити, чи збираємось ми повернути origCharабо newChar.


2

Ви можете розширити тип рядка, щоб включити метод вставки:

String.prototype.append = function (index,value) {
  return this.slice(0,index) + value + this.slice(index);
};

var s = "New string";
alert(s.append(4,"complete "));

Тоді ви можете викликати функцію:


1

Я зробив функцію, яка робить щось подібне до того, що ви запитуєте, вона перевіряє, чи є символ у рядку в масиві недозволених символів, якщо він замінює його на ''

    var validate = function(value){
        var notAllowed = [";","_",">","<","'","%","$","&","/","|",":","=","*"];
        for(var i=0; i<value.length; i++){
            if(notAllowed.indexOf(value.charAt(i)) > -1){
               value = value.replace(value.charAt(i), "");
               value = validate(value);
            }
       }
      return value;
   }

1

це легко досягти за допомогою RegExp!

const str = 'Hello RegEx!';
const index = 11;
const replaceWith = 'p';

//'Hello RegEx!'.replace(/^(.{11})(.)/, `$1p`);
str.replace(new RegExp(`^(.{${ index }})(.)`), `$1${ replaceWith }`);

//< "Hello RegExp"

1

Ось версія, яку я придумав, якщо ви хочете стилювати слова або окремі символи з їх індексу в реакції / JavaScript.

replaceAt( yourArrayOfIndexes, yourString/orArrayOfStrings ) 

Приклад роботи: https://codesandbox.io/s/ov7zxp9mjq

function replaceAt(indexArray, [...string]) {
    const replaceValue = i => string[i] = <b>{string[i]}</b>;
    indexArray.forEach(replaceValue);
    return string;
}

І ось ще один альтернативний метод

function replaceAt(indexArray, [...string]) {
    const startTag = '<b>';
    const endTag = '</b>';
    const tagLetter = i => string.splice(i, 1, startTag + string[i] + endTag);
    indexArray.forEach(tagLetter);
    return string.join('');
}

І ще ...

function replaceAt(indexArray, [...string]) {
    for (let i = 0; i < indexArray.length; i++) {
        string = Object.assign(string, {
          [indexArray[i]]: <b>{string[indexArray[i]]}</b>
        });
    }
    return string;
}

0

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

String.prototype.replaceAt=function(index, character) {
    if(index>-1) return this.substr(0, index) + character + this.substr(index+character.length);
    else return this.substr(0, this.length+index) + character + this.substr(index+character.length);

}

0

Скажімо, ви хочете замінити Kthіндекс (індекс на основі 0) на 'Z'. Ви можете використати Regexдля цього.

var re = var re = new RegExp("((.){" + K + "})((.){1})")
str.replace(re, "$1A$`");

Будь-яка ідея, чому цей тег не корисний?
ksp

Я не перевіряв, чи працює цей регулярний вираз, але, можливо, це спростовано, тому що, можливо, набагато швидше створити новий рядок, ніж компілювати стан-машину (регулярний вираз), що дуже дорого.
Felo Vilches

0

Ви можете використовувати наступну функцію для заміни Characterабо Stringв певному положенні рядка. Для заміни всіх наведених нижче випадків відповідності використовуйте String.prototype.replaceAllMatches()функцію.

String.prototype.replaceMatch = function(matchkey, replaceStr, matchIndex) {
    var retStr = this, repeatedIndex = 0;
    for (var x = 0; (matchkey != null) && (retStr.indexOf(matchkey) > -1); x++) {
        if (repeatedIndex == 0 && x == 0) {
            repeatedIndex = retStr.indexOf(matchkey);
        } else { // matchIndex > 0
            repeatedIndex = retStr.indexOf(matchkey, repeatedIndex + 1);
        }
        if (x == matchIndex) {
            retStr = retStr.substring(0, repeatedIndex) + replaceStr + retStr.substring(repeatedIndex + (matchkey.length));
            matchkey = null; // To break the loop.
        }
    }
    return retStr;
};

Тест:

var str = "yash yas $dfdas.**";

console.log('Index Matched replace : ', str.replaceMatch('as', '*', 2) );
console.log('Index Matched replace : ', str.replaceMatch('y', '~', 1) );

Вихід:

Index Matched replace :  yash yas $dfd*.**
Index Matched replace :  yash ~as $dfdas.**

-1

Ось моє рішення з використанням терміналу та оператора карт. Більш читабельний, ремонтований кінець простіше зрозуміти, якщо ви запитаєте мене.

Це більше стосується es6 та найкращих практик.

function replaceAt() {
  const replaceAt = document.getElementById('replaceAt').value;

  const str = 'ThisIsATestStringToReplaceCharAtSomePosition';
  const newStr = Array.from(str).map((character, charIndex) => charIndex === (replaceAt - 1) ? '' : character).join('');

  console.log(`New string: ${newStr}`);
}
<input type="number" id="replaceAt" min="1" max="44" oninput="replaceAt()"/>


-3

Методи тут складні. Я зробив би це так:

var myString = "this is my string";
myString = myString.replace(myString.charAt(number goes here), "insert replacement here");

Це так просто, як це виходить.


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