Czy istnieje metoda łączenia strun?

87

Javascript splicedziała tylko z tablicami. Czy istnieje podobna metoda dla stringów? A może powinienem utworzyć własną funkcję niestandardową?

Te substr()i substring()metody tylko zwrócić wyodrębniony i nie zmieniają oryginalnego łańcucha. To, co chcę zrobić, to usunąć część z mojego ciągu i zastosować zmianę do oryginalnego ciągu. Co więcej, metoda replace()nie zadziała w moim przypadku, ponieważ chcę usunąć części zaczynające się od indeksu, a kończące na jakimś innym indeksie, dokładnie tak, jak mogę zrobić z splice()metodą. Próbowałem przekonwertować mój ciąg na tablicę, ale nie jest to zgrabna metoda.

ProllyGeek
źródło
str.splitmoże to być funkcja, której szukasz: google.com/#q=javascript+string+split
Anderson Green
1
Co jest nie tak str = str.slice()?
elclanrs
1
Wygląda na problem XY, co dokładnie próbujesz zrobić? Łańcuchy nie są przekazywane przez referencje, w przeciwieństwie do tablic i obiektów, więc nie modyfikujesz łańcuchów, tworzysz nowe lub ponownie przypisujesz nowy do starego.
elclanrs
6
return string.slice(0, startToSplice) + string.slice(endToSplice);nie?
raina77ow

Odpowiedzi:

88

Szybciej jest dwukrotnie przeciąć strunę, na przykład:

function spliceSlice(str, index, count, add) {
  // We cannot pass negative indexes directly to the 2nd slicing operation.
  if (index < 0) {
    index = str.length + index;
    if (index < 0) {
      index = 0;
    }
  }

  return str.slice(0, index) + (add || "") + str.slice(index + count);
}

niż użycie podziału, po którym następuje złączenie (metoda Kumara Harsha), na przykład:

function spliceSplit(str, index, count, add) {
  var ar = str.split('');
  ar.splice(index, count, add);
  return ar.join('');
}

Oto plik jsperf, który porównuje te dwie metody i kilka innych. (jsperf nie działa już od kilku miesięcy. Prosimy o sugerowanie alternatyw w komentarzach).

Chociaż powyższy kod implementuje funkcje, które odtwarzają ogólną funkcjonalnośćsplice , optymalizacja kodu dla przypadku przedstawionego przez pytającego (to jest dodanie niczego do zmodyfikowanego ciągu) nie zmienia względnej wydajności różnych metod.

Louis
źródło
3
Możesz dodać to globalnie za pomocą: String.prototype.splice = function (index, count, add) { return this.slice(0, index) + (add || "") + this.slice(index + count); } Pamiętaj, że nie działa dokładnie tak samo jak metoda łączenia tablicy, ponieważ łańcuchy są niezmienne (nie możesz ich modyfikować po utworzeniu). Tak więc użycie byłoby następujące:var s="ss"; s = s.splice(0,1,"t");
William Neely,
@WilliamNeely: var StringUtils={'StringSplice':/*func def*/};byłby standardem do manipulacji napisami, ponieważ powiedziałeś, że jest niezmienny.
Mr. Polywhirl
spliceSlice i spliceSplit nie są funkcjonalnie równe, ponieważ Array.prototype.splice akceptuje ujemne indeksy: jsfiddle.net/sykteho6/5 .
Martijn,
1
@Mzialla Dzięki, dodałem kod, aby się tym zająć.
Louis
Tworzenie zmiennych tymczasowych i wprowadzanie miejsc dla błędów typu off-by-one, nie wspominając o kodzie, którego przetwarzanie zajmuje ludziom więcej czasu niż w przypadku split („”). Splice (...). Join („”) to zasługuje na uwagę. Problem z prędkością jest problemem tylko wtedy, gdy jest problemem.
Ben Fletcher,
15

Edytować

Nie jest to oczywiście najlepszy sposób na „łączenie” łańcucha, podałem to jako przykład tego, jak wyglądałaby implementacja, która jest wadliwa i bardzo widoczna w przypadku split (), splice () i join (). Aby uzyskać znacznie lepszą implementację, zobacz metodę Louisa .


Nie, nie ma czegoś takiego jak a String.splice, ale możesz spróbować tego:

newStr = str.split(''); // or newStr = [...str];
newStr.splice(2,5);
newStr = newStr.join('');

Zdaję sobie sprawę, że nie ma takiej splicefunkcji jak w Arrays, więc musisz przekonwertować ciąg na tablicę. Pech ...

kumarharsh
źródło
w moim przypadku im pracuję na dynamicznym ciągu, więc muszę określić indeksy, a nie wartość znaku.
ProllyGeek
Tak, to niestety prawda. Ale możesz uzyskać indeks znaku, używając String.prototype.indexOf as"asdsd".indexOf('s');
kumarharsh
3
Zamiast używać str.split, możesz użyć składni spreadu ES6 w następujący sposób:[...str]
robbie Kwietnia
5

Oto ładne, małe curry, które zapewnia lepszą czytelność (IMHO):

Podpis drugiej funkcji jest identyczny z Array.prototype.splicemetodą.

function mutate(s) {
    return function splice() {
        var a = s.split('');
        Array.prototype.splice.apply(a, arguments);
        return a.join('');
    };
}

mutate('101')(1, 1, '1');

Wiem, że jest już zaakceptowana odpowiedź, ale mam nadzieję, że jest przydatna.

Cody
źródło
1
Np. Możesz nawet wyabstrahować to, aby zezwolić, powiedzmy, na opcjonalny 'method'parametr, dzięki czemu możesz przekazać Array.prototype[method]ciągi i traktować je całkowicie jak tablice. Również z tego samego jednego, można streszczenie rzeczywisty proces do innej funkcji - a jeśli nie ma ciąg, powrót funkcję, która będzie miała ciąg i odwrócić curry: mutate()(1, 1, '1')('101'); Możesz nawet duckType argumenty, aby usunąć curry z pustego wywołania - a nawet hermetyzować samą metodę. Jeśli potrzebujesz tych rzeczy, możesz jednak zajrzeć do wzorca poleceń . Po prostu plujcie tutaj.
Cody
3

Chciałbym zaoferować prostszą alternatywę zarówno dla metody Kumar / Cody, jak i metody Louisa. We wszystkich testach, które przeprowadziłem, działa tak szybko, jak metoda Louisa (patrz testy skrzypiec dla testów porównawczych).

String.prototype.splice = function(startIndex,length,insertString){
    return this.substring(0,startIndex) + insertString + this.substring(startIndex + length);
};

Możesz go używać w ten sposób:

var creditCardNumber = "5500000000000004";
var cardSuffix = creditCardNumber.splice(0,12,'****');
console.log(cardSuffix);  // output: ****0004

Zobacz wyniki testu: https://jsfiddle.net/0quz9q9m/5/

Timothy Kanski
źródło
powinieneś przynajmniej zrobić `+ (insertString ||" ") +` zamiast `+ insertString + , otherwise the third argument is not optional. This implementation also doesn't take care of startIndex <0` w przeciwieństwie do innych sugestii. Świetny przykład użycia
JakowL
3

Wydaje się, że jest wiele nieporozumień, które zostały rozwiązane tylko w komentarzach elclanrs i raina77ow, więc pozwólcie, że opublikuję wyjaśniającą odpowiedź.

Wyjaśnienie

Z "string.splice ” można by się tego spodziewać, podobnie jak w przypadku tablic:

  • przyjmuje do 3 argumentów: pozycja początkowa, długość i (opcjonalnie) wstawienie (ciąg)
  • zwraca wyciętą część
  • modyfikuje oryginalny ciąg

Problem w tym, że wymaganie 3d nie może zostać spełnione, ponieważ ciągi znaków są niezmienne (powiązane: 1 , 2 ), najbardziej dedykowany komentarz znalazłem tutaj :

W JavaScript łańcuchy są prymitywnymi typami wartości, a nie obiektami ( specyfikacjami ). W rzeczywistości, jak z ES5, są jedną z zaledwie 5 typów wartości obok null, undefined, numberi boolean. Ciągi są przypisywane przez wartość, a nie przez odwołanie i jako takie są przekazywane. W ten sposób ciągi znaków są nie tylko niezmienne, są one wartością . Zmiana ciąg "hello"być "world"jakby uznając, że od teraz numer 3 to numer 4 ... to nie ma sensu.

Tak więc, biorąc to pod uwagę, można oczekiwać, że „ string.splice” tylko:

  • przyjmuje do 2 argumentów: pozycja początkowa, długość (wstawianie nie ma sensu, ponieważ ciąg nie jest zmieniany)
  • zwraca wyciętą część

co jest tym, co substrrobi; lub alternatywnie

  • przyjmuje do 3 argumentów: pozycja początkowa, długość i (opcjonalnie) wstawienie (ciąg)
  • zwraca zmodyfikowany ciąg (bez wyciętej części iz wstawieniem)

co jest tematem następnej sekcji.

Rozwiązania

Jeśli zależy Ci na optymalizacji, prawdopodobnie powinieneś skorzystać z implementacji Mike'a:

String.prototype.splice = function(index, count, add) {
    if (index < 0) {
        index += this.length;
        if (index < 0)
            index = 0;
    }
    return this.slice(0, index) + (add || "") + this.slice(index + count);
}

Traktowanie wskaźnika poza granicami może się jednak różnić. W zależności od potrzeb możesz chcieć:

    if (index < 0) {
        index += this.length;
        if (index < 0)
            index = 0;
    }
    if (index >= this.length) {
        index -= this.length;
        if (index >= this.length)
            index = this.length - 1;
    }

lub nawet

    index = index % this.length;
    if (index < 0)
        index = this.length + index;

Jeśli nie zależy Ci na wydajności, możesz dostosować sugestię Kumara, która jest prostsza:

String.prototype.splice = function(index, count, add) {
    var chars = this.split('');
    chars.splice(index, count, add);
    return chars.join('');
}

Występ

Różnica w wykonaniu drastycznie rośnie wraz z długością struny. jsperf pokazuje , że dla łańcuchów o długości 10 to drugie rozwiązanie (dzielenie i łączenie) jest dwukrotnie wolniejsze niż poprzednie rozwiązanie (z użyciem plasterka), dla 100-literowych łańcuchów to x5, a dla 1000-literowych to x50, w Ops / s to:

                      10 letters   100 letters   1000 letters
slice implementation    1.25 M       2.00 M         1.91 M
split implementation    0.63 M       0.22 M         0.04 M

zwróć uwagę, że zmieniłem argumenty 1. i 2. przy przechodzeniu z 10 liter na 100 liter (nadal jestem zaskoczony, że test na 100 liter działa szybciej niż na 10 liter).

YakovL
źródło
2

Po prostu użyj substr jako string

dawny.

var str = "Hello world!";
var res = str.substr(1, str.length);

Wynik = świat ello!

Dinesh Patil
źródło
1

Tak więc, niezależnie od dodania metody łączenia do prototypu String, nie można działać przezroczysto zgodnie ze specyfikacją ...

Zróbmy to komuś przedłużyć:

String.prototype.splice = function(...a){
    for(var r = '', p = 0, i = 1; i < a.length; i+=3)
        r+= this.slice(p, p=a[i-1]) + (a[i+1]||'') + this.slice(p+a[i], p=a[i+2]||this.length);
    return r;
}
  • Co 3 grupy argumentów „wstawiające” w stylu łączenia.
  • Specjalnie, jeśli jest więcej niż jedna grupa 3 argumentów, koniec każdego cięcia będzie początkiem następnego.
    • „0123456789”. Podwójny (4,1, „czwarty”, 8,1, „ósmy”); // zwraca „0123fourth567eighth9”
  • Możesz upuścić lub wyzerować ostatni argument w każdej grupie (traktowany jako „nic do wstawienia”)
    • '0123456789'.plice (-4,2); // zwróć „0123459”
  • Możesz usunąć wszystkie z wyjątkiem pierwszego argumentu w ostatniej grupie (traktowane jako „wytnij wszystko po pierwszej pozycji argumentu”)
    • „0123456789”. Podwójna (0,2, null, 3,1, null, 5,2, „/”, 8); // powrót '24 / 7 '
  • jeśli zdasz wiele, MUSISZ samodzielnie sprawdzić rodzaj pozycji od lewej do prawej!
  • A jeśli nie chcesz, NIE WOLNO go używać w ten sposób:
    • '0123456789'.splice (4, -3,' co? '); // zwraca „0123what? 123456789”
Fedor Tykmakov
źródło
0

Metoda odpowiedzi Louisa , jako Stringfunkcja prototypowa:

String.prototype.splice = function(index, count, add) {
  if (index < 0) {
    index = this.length + index;
    if (index < 0) {
      index = 0;
    }
  }
  return this.slice(0, index) + (add || "") + this.slice(index + count);
}

Przykład:

 > "Held!".splice(3,0,"lo Worl")
 < "Hello World!"
Mike Godin
źródło
0

Metoda spliceSlice Louisa zawodzi, gdy wartość dodania wynosi 0 lub inne fałszywe wartości, oto poprawka:

function spliceSlice(str, index, count, add) {
  if (index < 0) {
    index = str.length + index;
    if (index < 0) {
      index = 0;
    }
  }
  const hasAdd = typeof add !== 'undefined';
  return str.slice(0, index) + (hasAdd ? add : '') + str.slice(index + count);
}
Maryus Martsyalis
źródło
0

Rozwiązałem swój problem za pomocą tego kodu, jest to swego rodzaju zamiennik brakującego splotu.

let str = "I need to remove a character from this";
let pos = str.indexOf("character")

if(pos>-1){
  let result = str.slice(0, pos-2) + str.slice(pos, str.length);
  console.log(result) //I need to remove character from this 
}

Musiałem usunąć znak przed / po pewnym słowie, po uzyskaniu pozycji ciąg jest dzielony na dwie, a następnie ponownie składany przez elastyczne usuwanie znaków za pomocą przesunięcia wzdłuż pos

Ray Remnant
źródło