599 Stimmen

Wie kehrt man in JavaScript eine Zeichenkette an Ort und Stelle um?

Wie kehrt man eine Zeichenkette in JavaScript an Ort und Stelle um, wenn sie an eine Funktion mit einer Rückgabeanweisung übergeben wird, ohne integrierte Funktionen zu verwenden ( .reverse() , .charAt() usw.)?

0 Stimmen

Sie dürfen also nicht .charAt() verwenden, um die Zeichen der Zeichenkette zu erhalten?

192 Stimmen

Das können Sie nicht. JavaScript-Zeichenfolgen sind unveränderlich, was bedeutet, dass der Speicher, der ihnen zugewiesen ist, nicht beschrieben werden kann, was echte "In-Place"-Umkehrungen unmöglich macht.

3 Stimmen

Re: crescentfresh's Kommentar siehe stackoverflow.com/questions/51185/

47voto

Michał Perłakowski Punkte 79585

Erstens, verwenden Sie Array.from() um eine Zeichenkette in ein Array zu verwandeln, dann Array.prototype.reverse() um das Array umzukehren, und dann Array.prototype.join() um daraus wieder eine Zeichenkette zu machen.

const reverse = str => Array.from(str).reverse().join('');

30voto

programking Punkte 1356

In ECMAScript 6 können Sie eine Zeichenkette noch schneller umkehren, ohne .split('') Split-Methode, mit dem Spread-Operator etwa so:

var str = [...'racecar'].reverse().join('');

22voto

ninjagecko Punkte 82995

Scheint so, als wäre ich 3 Jahre zu spät zur Party gekommen...

Wie bereits erwähnt, ist das leider nicht möglich. Siehe Sind JavaScript-Zeichenfolgen unveränderlich? Brauche ich einen "String Builder" in JavaScript?

Das nächstbeste, was Sie tun können, ist, eine "Ansicht" oder einen "Wrapper" zu erstellen, der eine Zeichenkette nimmt und alle Teile der von Ihnen verwendeten Zeichenketten-API neu implementiert, aber so tut, als ob die Zeichenkette umgekehrt wäre. Zum Beispiel:

var identity = function(x){return x};

function LazyString(s) {
    this.original = s;

    this.length = s.length;
    this.start = 0; this.stop = this.length; this.dir = 1; // "virtual" slicing
    // (dir=-1 if reversed)

    this._caseTransform = identity;
}

// syntactic sugar to create new object:
function S(s) {
    return new LazyString(s);
}

//We now implement a `"...".reversed` which toggles a flag which will change our math:

(function(){ // begin anonymous scope
    var x = LazyString.prototype;

    // Addition to the String API
    x.reversed = function() {
        var s = new LazyString(this.original);

        s.start = this.stop - this.dir;
        s.stop = this.start - this.dir;
        s.dir = -1*this.dir;
        s.length = this.length;

        s._caseTransform = this._caseTransform;
        return s;
    }

//We also override string coercion for some extra versatility (not really necessary):

    // OVERRIDE STRING COERCION
    //   - for string concatenation e.g. "abc"+reversed("abc")
    x.toString = function() {
        if (typeof this._realized == 'undefined') {  // cached, to avoid recalculation
            this._realized = this.dir==1 ?
                this.original.slice(this.start,this.stop) : 
                this.original.slice(this.stop+1,this.start+1).split("").reverse().join("");

            this._realized = this._caseTransform.call(this._realized, this._realized);
        }
        return this._realized;
    }

//Now we reimplement the String API by doing some math:

    // String API:

    // Do some math to figure out which character we really want

    x.charAt = function(i) {
        return this.slice(i, i+1).toString();
    }
    x.charCodeAt = function(i) {
        return this.slice(i, i+1).toString().charCodeAt(0);
    }

// Slicing functions:

    x.slice = function(start,stop) {
        // lazy chaining version of https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/slice

        if (stop===undefined)
            stop = this.length;

        var relativeStart = start<0 ? this.length+start : start;
        var relativeStop = stop<0 ? this.length+stop : stop;

        if (relativeStart >= this.length)
            relativeStart = this.length;
        if (relativeStart < 0)
            relativeStart = 0;

        if (relativeStop > this.length)
            relativeStop = this.length;
        if (relativeStop < 0)
            relativeStop = 0;

        if (relativeStop < relativeStart)
            relativeStop = relativeStart;

        var s = new LazyString(this.original);
        s.length = relativeStop - relativeStart;
        s.start = this.start + this.dir*relativeStart;
        s.stop = s.start + this.dir*s.length;
        s.dir = this.dir;

        //console.log([this.start,this.stop,this.dir,this.length], [s.start,s.stop,s.dir,s.length])

        s._caseTransform = this._caseTransform;
        return s;
    }
    x.substring = function() {
        // ...
    }
    x.substr = function() {
        // ...
    }

//Miscellaneous functions:

    // Iterative search

    x.indexOf = function(value) {
        for(var i=0; i<this.length; i++)
            if (value==this.charAt(i))
                return i;
        return -1;
    }
    x.lastIndexOf = function() {
        for(var i=this.length-1; i>=0; i--)
            if (value==this.charAt(i))
                return i;
        return -1;
    }

    // The following functions are too complicated to reimplement easily.
    // Instead just realize the slice and do it the usual non-in-place way.

    x.match = function() {
        var s = this.toString();
        return s.apply(s, arguments);
    }
    x.replace = function() {
        var s = this.toString();
        return s.apply(s, arguments);
    }
    x.search = function() {
        var s = this.toString();
        return s.apply(s, arguments);
    }
    x.split = function() {
        var s = this.toString();
        return s.apply(s, arguments);
    }

// Case transforms:

    x.toLowerCase = function() {
        var s = new LazyString(this.original);
        s._caseTransform = ''.toLowerCase;

        s.start=this.start; s.stop=this.stop; s.dir=this.dir; s.length=this.length;

        return s;
    }
    x.toUpperCase = function() {
        var s = new LazyString(this.original);
        s._caseTransform = ''.toUpperCase;

        s.start=this.start; s.stop=this.stop; s.dir=this.dir; s.length=this.length;

        return s;
    }

})() // end anonymous scope

Demo:

> r = S('abcABC')
LazyString
  original: "abcABC"
  __proto__: LazyString

> r.charAt(1);       // doesn't reverse string!!! (good if very long)
"B"

> r.toLowerCase()    // must reverse string, so does so
"cbacba"

> r.toUpperCase()    // string already reversed: no extra work
"CBACBA"

> r + '-demo-' + r   // natural coercion, string already reversed: no extra work
"CBAcba-demo-CBAcba"

Der Clou: Die folgenden Schritte werden durch reine Mathematik an Ort und Stelle ausgeführt, wobei jedes Zeichen nur einmal und nur bei Bedarf besucht wird:

> 'demo: ' + S('0123456789abcdef').slice(3).reversed().slice(1,-1).toUpperCase()
"demo: EDCBA987654"

> S('0123456789ABCDEF').slice(3).reversed().slice(1,-1).toLowerCase().charAt(3)
"b"

Dies führt zu erheblichen Einsparungen, wenn man nur einen relativ kleinen Teil einer sehr großen Zeichenkette abnimmt.

Ob sich dies lohnt (im Gegensatz zu "Reversing-as-a-copy" wie in den meisten Programmiersprachen) hängt stark von Ihrem Anwendungsfall ab und davon, wie effizient Sie die String-API neu implementieren. Wenn Sie zum Beispiel nur den String-Index manipulieren wollen oder kleine slice s oder substr s, das spart Platz und Zeit. Wenn Sie jedoch vorhaben, große umgekehrte Slices oder Teilstrings zu drucken, kann die Ersparnis in der Tat gering sein, sogar schlimmer als bei einer vollständigen Kopie. Ihre "umgekehrte" Zeichenkette hat auch nicht den Typ string Allerdings könnte man dies mit Hilfe von Prototypen vortäuschen.

Die obige Demo-Implementierung erzeugt ein neues Objekt vom Typ ReversedString. Es handelt sich um eine Prototyp-Implementierung, die daher recht effizient ist, mit fast minimalem Aufwand und minimalem Platzbedarf (Prototyp-Definitionen werden gemeinsam genutzt). Es handelt sich um eine "lazy"-Implementierung mit "deferred slicing". Wann immer Sie eine Funktion wie .slice ou .reversed wird sie Indexmathematik betreiben. Wenn Sie schließlich Daten extrahieren (durch impliziten Aufruf von .toString() ou .charCodeAt(...) oder ähnliches), wird es diese auf "intelligente" Weise anwenden und dabei so wenig Daten wie möglich berühren.

Hinweis: Die obige String-API ist ein Beispiel, das möglicherweise nicht perfekt implementiert ist. Sie können auch nur 1-2 Funktionen verwenden, die Sie benötigen.

20voto

Dr.G Punkte 389

Lesbare Form unter Verwendung der Spreizsyntax:

const reverseString = str => [...str].reverse().join('');

console.log(reverseString('ABC'));

17voto

Es gibt viele Möglichkeiten, wie man eine Zeichenkette in JavaScript umkehren kann. Ich schreibe hier drei Möglichkeiten auf, die ich bevorzuge.

Ansatz 1: Verwendung der Umkehrfunktion:

function reverse(str) {
  return str.split('').reverse().join('');
}

Ansatz 2: Schleifenbildung durch Zeichen:

function reverse(str) {
  let reversed = '';

  for (let character of str) {
    reversed = character + reversed;
  }

  return reversed;
}

Ansatz 3: Verwendung der Reduzierfunktion:

function reverse(str) {
  return str.split('').reduce((rev, char) => char + rev, '');
}

Ich hoffe, das hilft :)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X