Jak zamienić znak w określonym indeksie w JavaScript?

544

Mam ciąg, powiedzmy, Hello worldi muszę zastąpić znak w indeksie 3. Jak mogę zastąpić znak przez określenie indeksu?

var str = "hello world";

Potrzebuję czegoś takiego

str.replaceAt(0,"h");
Santhosh
źródło
131
Dziwne jest to, że str[0] = 'x'wydaje się, że nie powoduje żadnych błędów, ale nie ma pożądanego efektu!
Michael
6
@Michael z tym, że uzyskasz indeks na 0, ustawisz go na „x”, ta instrukcja sama w sobie zwróci nową wartość; „x”. ale wszystko to nie zmienia oryginału, więc jest całkowicie poprawny, tylko nie tak, jak się spodziewałeś. to nie jest odniesienie
Paul Scheltema
8
@Michael robi, jeśli "use strict"jest aktywowany: Uncaught TypeError: Cannot assign to read only property '0' of string 'hello world'(przynajmniej w przeglądarkach internetowych)
Jan Turoň
1
Ciągi JavaScript są niezmienne, nie można ich modyfikować „na miejscu”, więc nie można modyfikować pojedynczego znaku. w rzeczywistości każde wystąpienie tego samego łańcucha jest JEDNYM obiektem.
Martijn Scheffer,
ok, zobacz odpowiedź: D
Martijn Scheffer

Odpowiedzi:

611

W JavaScript ciągi są niezmienne , co oznacza, że ​​najlepiej, co możesz zrobić, to utworzyć nowy ciąg ze zmienioną zawartością i przypisać zmienną, która będzie do niego wskazywać.

Musisz samodzielnie zdefiniować replaceAt()funkcję:

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

I użyj tego w ten sposób:

var hello = "Hello World";
alert(hello.replaceAt(2, "!!")); // Should display He!!o World
Cem Kalyoncu
źródło
101
Pamiętaj, że generalnie nie jest dobrym pomysłem rozszerzanie podstawowych klas JavaScript. Zamiast tego użyj zwykłej funkcji narzędzia.
Ates Goral
86
Muszę zapytać dlaczego? W tym celu istnieje wsparcie prototypów.
Cem Kalyoncu
30
@CemKalyoncu: Może sprawić, że kod będzie działał źle z innymi bibliotekami. Ale sam nigdy mnie nie ugryzł.
alex
6
@alex: masz rację, naprawdę musisz sprawdzić, czy ta funkcja istnieje, zanim to zrobisz. Ale nawet jeśli tak jest, powinien wykonać dokładnie tę samą operację.
Cem Kalyoncu
80
Nie zgadzam się, nawet jeśli wykryjesz, czy funkcja istnieje, późniejsza biblioteka może nadal używać / tworzyć podobną funkcję - 2 złe pomysły, które pogarszają. Ogólnie rzecz biorąc, nigdy nie baw się kodem innych ludzi (w tym systemem podstawowym). Utwórz nową funkcję.
martyman
93

W replaceAtJavaScript nie ma żadnej funkcji. Możesz użyć następującego kodu, aby zastąpić dowolny znak w dowolnym ciągu w określonej pozycji:

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>

Rahul
źródło
Preferuję to rozwiązanie, ponieważ możesz zastąpić nim jedną postać. Rozwiązanie, które ma najwięcej pozytywnych opinii, nie działa, jeśli chcesz zastąpić tylko jedną postać. Działa tylko przy zamianie dwóch postaci! Ponadto substr należy zastąpić substringiem, jak wspomniano w wielu innych komentarzach.
gignu
74

Nie możesz Weź znaki przed i po pozycji i konkatuj w nowy ciąg:

var s = "Hello world";
var index = 3;
s = s.substring(0, index) + 'x' + s.substring(index + 1);
Guffa
źródło
Właściwie tak, możesz, ale byłoby to przesadą. Możesz ustawić wyrażenie regularne, aby pominąć pierwszy indeks - 1 znaki i dopasować znak do indeksu. Możesz użyć String.replace z tym wyrażeniem regularnym, aby dokonać bezpośredniej zamiany w miejscu. Ale to przesada. Więc w praktyce nie możesz. Ale teoretycznie możesz.
Ates Goral
12
@Ates: Funkcja replace nie zastępuje na miejscu, tworzy nowy ciąg znaków i zwraca go.
Guffa
2
MDN sugeruje użycie String.prototype.substringponad String.prototype.substr.
Mikemike,
33

Tutaj jest wiele odpowiedzi i wszystkie oparte są na dwóch metodach:

  • METODA 1: podziel ciąg przy użyciu dwóch podciągów i umieść między nimi znak
  • METODA2: przekonwertuj ciąg na tablicę znaków, zamień jeden element tablicy i dołącz do niego

Osobiście użyłbym tych dwóch metod w różnych przypadkach. Pozwól mi wyjaśnić.

@FabioPhms: Twoja metoda była tą, której początkowo użyłem, i bałam się, że źle działa na sznurku z dużą ilością znaków. Pytanie brzmi jednak, ile jest znaków? Przetestowałem go na 10 akapitach „lorem ipsum” i zajęło to kilka milisekund. Następnie przetestowałem go na 10-krotnie większym sznurku - naprawdę nie było dużej różnicy. Hm

@vsync, @Cory Mawhorter: Twoje komentarze są jednoznaczne; jednak, co to jest duży ciąg? Zgadzam się, że dla wydajności 32 ... 100 kb powinno być lepiej i należy użyć wariantu podłańcuchowego dla tej jednej operacji zamiany znaków.

Ale co się stanie, jeśli będę musiał zrobić sporo zamienników?

Musiałem przeprowadzić własne testy, aby udowodnić, co jest szybsze w tym przypadku. Powiedzmy, że mamy algorytm, który będzie manipulował stosunkowo krótkim łańcuchem, który składa się z 1000 znaków. Oczekujemy, że średnio każdy znak w tym ciągu zostanie zastąpiony ~ 100 razy. Tak więc kod do testowania czegoś takiego to:

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*
}

Stworzyłem do tego skrzypce i już tu jest . Istnieją dwa testy, TEST1 (substring) i TEST2 (konwersja tablicy).

Wyniki:

  • TEST1: 195ms
  • TEST2: 6ms

Wygląda na to, że konwersja tablicy pokonuje podciąg o 2 rzędy wielkości! Więc - co tu się do cholery stało?

W rzeczywistości wszystkie operacje w TEST2 są wykonywane na samej tablicy, przy użyciu wyrażenia przypisania, takiego jak strarr2[p] = n. Przypisanie jest naprawdę szybkie w porównaniu z podciągiem na dużym sznurku i jasne, że wygra.

Chodzi więc o wybranie odpowiedniego narzędzia do pracy. Jeszcze raz.

OzrenTkalcecKrznaric
źródło
7
Przenieś swój test do jsperf, uzyskując bardzo różne wyniki: jsperf.com/string-replace-character . Chodzi o wybranie odpowiedniego narzędzia do pracy;)
colllin
1
@colllin: Absolutnie się zgadzam. Więc prawie dosłownie sklonowałem testy z jsfiddle do jsperf. Jednak oprócz korzystania z odpowiedniego narzędzia musisz to zrobić we właściwy sposób. Zwróć uwagę na problem, mówimy o tym, co się stanie, jeśli będziemy musieli dokonać kilku zmian w algorytmie. Konkretny przykład obejmuje 10000 zamienników. Oznacza to, że jeden test powinien obejmować 10000 zamienników. Korzystając z jsperf, uzyskałem dość spójne wyniki, patrz: jsperf.com/… .
OzrenTkalcecKrznaric
1
dlatego nie lubię javascript. tak częste, intuicyjne i oczywiste przypadki użycia nie są przewidziane. Można by pomyśleć, że przydarzyłoby się autorom javascript, że ludzie mogą chcieć podstawiać znaki w łańcuchach i że zdecydują się na wprowadzenie wygodnych metod w języku.
ahnbizcad
1
@colllin zrobił coś innego niż OzrenTkalcecKrznaric. OzrenTkalcecKrznaric założył, że ciąg jest statyczny, a podział można zoptymalizować przed uruchomieniami. Co niekoniecznie jest prawdą. jeśli musisz rozdzielić przy każdym uruchomieniu, podejście do podciągania jest około dwa razy 4x szybsze na małych strunach i tylko szybciej, tym większy staje się struna. Moim zdaniem sprawia to, że podciąg jest lepszą implementacją dla większości przypadków użycia, z wyjątkiem dużej liczby zmian na dużym ciągu statycznym.
WiR3D
3
Problem polega na tym, że w teście 2 rozdzielanie i łączenie znajdują się poza pętlą for, w której porównujesz zamianę pojedynczego znaku z wielokrotnym (niesprawiedliwym), pierwsza metoda jest szybsza dla pojedynczych zamian, a druga jest lepsza dla łańcuchowych zamian jedna po drugiej
Θεόφιλος Μουρατίδης
32

Praca z wektorami jest zwykle najskuteczniejsza w kontaktowaniu się z ciągiem znaków.

Proponuję następującą funkcję:

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

Uruchom ten fragment kodu:

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

Fabio Phms
źródło
9
jest to złe w przypadku dużych ciągów, nie trzeba tworzyć tablicy z szaloną ilością komórek, jeśli można po prostu użyć substra, aby go wyciąć ...
vsync
1
Przydatne, jeśli musisz zmienić wiele znaków, i to jest proste.
Sheepy
6
Stworzyłem test na jsperf: jsperf.com/replace-character-by-index - Substr jest znacznie szybszy i spójny od 32 bajtów do 100kb. Głosowałem za tym i choć boli mnie to, że jest to przyjemniejsze, substrat jest lepszym wyborem dla strun dowolnego rozmiaru.
Cory Mawhorter,
To jest dość nieoptymalne.
Radko Dinev,
Jest to dobre szybkie rozwiązanie, jeśli wiesz, że zamierzasz używać małych łańcuchów. Jeśli zgadzasz się na rozszerzenie prototypów, możesz osiągnąć podobne zachowanie w 2 liniach:Array.prototype.replace = function(index, val) { this[index] = val; return this; }; 'word'.split('').replace(1, '0').join(''); // returns 'w0rd'
Michael Plautz
29

W JavaScript ciągi są niezmienne, więc musisz zrobić coś takiego

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

Aby zamienić znak w x at i na „h”

DevDevDev
źródło
28
str = str.split('');
str[3] = 'h';
str = str.join('');
colllin
źródło
4
Jasne i proste, ale obecnie nie działa z emoji (takimi jak 😀) podczas używania splitijoin
Green
1
Z es6 myślę, że Array.from (str) może być teraz lepszą opcją, ale przyszedłem tutaj z tą wiedzą i nauczyłem się czegoś równie zwięzłego, więc dziękuję!
David Kamer
7

Jednowarstwowy przy użyciu String.replace z oddzwanianiem (brak obsługi emoji):

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

Wyjaśnione:

//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
})
Afanasii Kurakin
źródło
6

Ta metoda jest dobra w przypadku ciągów o małej długości, ale może być powolna w przypadku większego tekstu.

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. 
*/
Vikramaditya
źródło
3

@CemKalyoncu: Dzięki za świetną odpowiedź!

Zmodyfikowałem go również nieco, aby bardziej przypominał metodę Array.splice (i wziąłem pod uwagę uwagę @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!"
Scrimothy
źródło
Nie jestem pewien, dlaczego zostało to odrzucone. To wiarygodna odpowiedź. Działa jak wskazano. I jest zgodny z Array.splicefunkcjonalnością typu.
Scrimothy,
3

Działa to podobnie do Array.splice:

String.prototype.splice = function (i, j, str) {
    return this.substr(0, i) + str + this.substr(j, this.length);
};
Aram Kocharyan
źródło
3

Możesz spróbować

var strArr = str.split("");

strArr[0] = 'h';

str = strArr.join("");
Mehulkumar
źródło
2

Jeśli chcesz zamienić znaki w łańcuchu, powinieneś utworzyć łańcuchy zmienne. Są to zasadniczo tablice znaków. Możesz stworzyć fabrykę:

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

Następnie możesz uzyskać dostęp do znaków, a cała tablica konwertuje się na ciąg znaków, gdy zostanie użyty jako ciąg:

  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!"
Jan Turoň
źródło
2

Uogólniając odpowiedź Afanasii Kurakina, mamy:

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

Rozwińmy i wyjaśnijmy zarówno wyrażenie regularne, jak i funkcję zamiennika:

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

Wyrażenie regularne .pasuje dokładnie do jednego znaku. To gsprawia, że ​​pasuje do każdego znaku w pętli for. replacerFunkcja nazywana jest podana zarówno pierwotny charakter i gdzie indeks że postać jest w struny. Wykonujemy proste ifstwierdzenie celu ustalenia, czy mamy zamiar wrócić albo origCharalbo newChar.

Stephen Quan
źródło
2

Możesz rozszerzyć typ ciągu o metodę wstawki:

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

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

Następnie możesz wywołać funkcję:

mbadeveloper
źródło
1

Zrobiłem funkcję, która robi coś podobnego do tego, o co pytasz, sprawdza, czy znak w ciągu znajduje się w tablicy niedozwolonych znaków, jeśli zastępuje go znakiem „”

    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;
   }
mirzak
źródło
1

jest to łatwo osiągalne dzięki 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"
Madmadi
źródło
1

Oto wersja, którą wymyśliłem, jeśli chcesz stylizować słowa lub pojedyncze znaki według ich indeksu w reag / javascript.

replaceAt( yourArrayOfIndexes, yourString/orArrayOfStrings ) 

Przykład roboczy: https://codesandbox.io/s/ov7zxp9mjq

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

A oto kolejna alternatywna metoda

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

I kolejny...

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;
}
Matt Wright
źródło
0

Wiem, że to stare, ale rozwiązanie nie działa dla indeksu ujemnego, więc dodałem do niego łatkę. mam nadzieję, że to komuś pomoże

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

}
cesar moro
źródło
0

Powiedzmy, że chcesz zastąpić Kthindeks (indeks oparty na 0) na 'Z'. Możesz to Regexzrobić.

var re = var re = new RegExp("((.){" + K + "})((.){1})")
str.replace(re, "$1A$`");
ksp
źródło
Wiesz, dlaczego to oznaczenie nie jest przydatne?
ksp
Nie sprawdziłem, czy to wyrażenie regularne działa, ale być może jest ono przegłosowane, ponieważ prawdopodobnie jest znacznie szybsze tworzenie nowego ciągu, niż kompilowanie automatu stanów (wyrażenie regularne), co jest bardzo drogie.
Felo Vilches,
0

Możesz użyć następującej funkcji do zamiany Characterlub Stringw określonej pozycji ciągu. Aby zastąpić wszystkie poniższe przypadki dopasowania, użyj String.prototype.replaceAllMatches()funkcji.

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

Test:

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

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

Wynik:

Index Matched replace :  yash yas $dfd*.**
Index Matched replace :  yash ~as $dfdas.**
Yash
źródło
-1

Oto moje rozwiązanie przy użyciu operatora trójskładnikowego i mapowego. Bardziej czytelny, łatwiejszy do utrzymania koniec łatwiejszy do zrozumienia, jeśli mnie o to poprosisz.

Bardziej dotyczy ES6 i najlepszych praktyk.

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

karlo1zg
źródło
-3

Metody tutaj są skomplikowane. Zrobiłbym to w ten sposób:

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

To jest tak proste, jak to możliwe.

Mikey
źródło
6
Nie aktualne. W rzeczywistości zastąpi pierwsze wystąpienie postaci. Wciąż źle.
Tomáš Zato - Przywróć Monikę