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.)?
Antworten
Zu viele Anzeigen?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('');
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('');
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.
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 :)
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/
3 Stimmen
@crescentfresh Sie sollten das als neue Antwort posten.
0 Stimmen
@Irwin, es ist 2015 und charAt() wird immer noch nicht vollständig unterstützt. Wo es unterstützt wird, ist es langsam.
2 Stimmen
Umkehrung einer Zeichenkette auf 3 Arten in Javascript
0 Stimmen
var reverse = (str, rev = '') => str.length == 0 ? console.log(rev) : reverse((str.substr(0,str.length -1)), (rev + str.substr(str.length -1 , 1)));
0 Stimmen
string umkehren in js