How do you reverse a string in-place in JavaScript when it is passed to a function with a return statement, without using built-in functions (.reverse(), .charAt() etc.)?
How do you reverse a string in-place in JavaScript when it is passed to a function with a return statement, without using built-in functions (.reverse(), .charAt() etc.)?
As long as you're dealing with simple ASCII characters, and you're happy to use built-in functions, this will work:
function reverse(s){
return s.split("").reverse().join("");
}
If you need a solution that supports UTF-16 or other multi-byte characters, be aware that this function will give invalid unicode strings, or valid strings that look funny. You might want to consider this answer instead.
[...s] is Unicode aware, a small edit gives:-
function reverse(s){
return [...s].reverse().join("");
}
String.prototype.reverse_string=function() {return this.split("").reverse().join("");}
or
String.prototype.reverse_string = function() {
var s = "";
var i = this.length;
while (i>0) {
s += this.substring(i-1,i);
i--;
}
return s;
}
Seems like I'm 3 years late to the party...
Unfortunately you can't as has been pointed out. See Are JavaScript strings immutable? Do I need a "string builder" in JavaScript?
The next best thing you can do is to create a "view" or "wrapper", which takes a string and reimplements whatever parts of the string API you are using, but pretending the string is reversed. For example:
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"
The kicker -- the following is done in-place by pure math, visiting each character only once, and only if necessary:
> 'demo: ' + S('0123456789abcdef').slice(3).reversed().slice(1,-1).toUpperCase()
"demo: EDCBA987654"
> S('0123456789ABCDEF').slice(3).reversed().slice(1,-1).toLowerCase().charAt(3)
"b"
This yields significant savings if applied to a very large string, if you are only taking a relatively small slice thereof.
Whether this is worth it (over reversing-as-a-copy like in most programming languages) highly depends on your use case and how efficiently you reimplement the string API. For example if all you want is to do string index manipulation, or take small slices or substrs, this will save you space and time. If you're planning on printing large reversed slices or substrings however, the savings may be small indeed, even worse than having done a full copy. Your "reversed" string will also not have the type string, though you might be able to fake this with prototyping.
The above demo implementation creates a new object of type ReversedString. It is prototyped, and therefore fairly efficient, with almost minimal work and minimal space overhead (prototype definitions are shared). It is a lazy implementation involving deferred slicing. Whenever you perform a function like .slice or .reversed, it will perform index mathematics. Finally when you extract data (by implicitly calling .toString() or .charCodeAt(...) or something), it will apply those in a "smart" manner, touching the least data possible.
Note: the above string API is an example, and may not be implemented perfectly. You also can use just 1-2 functions which you need.
Legible way using spread syntax:
const reverseString = str => [...str].reverse().join('');
console.log(reverseString('ABC'));
There are many ways you can reverse a string in JavaScript. I'm jotting down three ways I prefer.
Approach 1: Using reverse function:
function reverse(str) {
return str.split('').reverse().join('');
}
Approach 2: Looping through characters:
function reverse(str) {
let reversed = '';
for (let character of str) {
reversed = character + reversed;
}
return reversed;
}
Approach 3: Using reduce function:
function reverse(str) {
return str.split('').reduce((rev, char) => char + rev, '');
}
I hope this helps :)
There are Multiple ways of doing it, you may check the following,
1. Traditional for loop(incrementing):
function reverseString(str){
let stringRev ="";
for(let i= 0; i<str.length; i++){
stringRev = str[i]+stringRev;
}
return stringRev;
}
alert(reverseString("Hello World!"));
2. Traditional for loop(decrementing):
function reverseString(str){
let revstr = "";
for(let i = str.length-1; i>=0; i--){
revstr = revstr+ str[i];
}
return revstr;
}
alert(reverseString("Hello World!"));
3. Using for-of loop
function reverseString(str){
let strn ="";
for(let char of str){
strn = char + strn;
}
return strn;
}
alert(reverseString("Get well soon"));
4. Using the forEach/ high order array method:
function reverseString(str){
let revSrring = "";
str.split("").forEach(function(char){
revSrring = char + revSrring;
});
return revSrring;
}
alert(reverseString("Learning JavaScript"));
5. ES6 standard:
function reverseString(str){
let revSrring = "";
str.split("").forEach(char => revSrring = char + revSrring);
return revSrring;
}
alert(reverseString("Learning JavaScript"));
6. The latest way:
function reverseString(str){
return str.split("").reduce(function(revString, char){
return char + revString;
}, "");
}
alert(reverseString("Learning JavaScript"));
7. You may also get the result using the following,
function reverseString(str){
return str.split("").reduce((revString, char)=> char + revString, "");
}
alert(reverseString("Learning JavaScript"));
In ES6, you have one more option
function reverseString (str) {
return [...str].reverse().join('')
}
reverseString('Hello');
If you don't want to use any built in function. Try this
var string = 'abcdefg';
var newstring = '';
for(let i = 0; i < string.length; i++){
newstring = string[i] += newstring;
}
console.log(newstring);
You can't because JS strings are immutable. Short non-in-place solution
[...str].reverse().join``
let str = "Hello World!";
let r = [...str].reverse().join``;
console.log(r);
One new option is to use Intl.Segmenter which allows you to split on the visual graphemes (ie: user-perceived character units such as emojis, characters, etc.). Intl.Segmenter is currently a stage 4 proposal and there is a polyfill available for it if you wish to use it. It currently has limited browser support which you can find more information about here.
Here is how the reverse() method may look if you use Intl.Segmenter:
const reverse = str => {
const segmenter = new Intl.Segmenter("en", {granularity: 'grapheme'});
const segitr = segmenter.segment(str);
const segarr = Array.from(segitr, ({segment}) => segment).reverse();
return segarr.join('');
}
console.log(reverse('foo bar mañana mañana')); // anañam anañam rab oof
console.log(reverse('This emoji is happy')); // yppah si ijome sihT
console.log(reverse('Text surrogate pair composite pair möo varient selector ❤️ & ZWJ ')); // JWZ & ❤️ rotceles tneirav oöm riap etisopmoc riap etagorrus txeT
The above creates a segmenter to segment/split strings by their visual graphemes. Calling .segment() on the segmenter with the string input then returns an iterator, which produces objects of the form {segment, index, input, isWordLike}. The segment key from this object contains the string segment (ie: the individual grapheme). To convert the iterator to an array, we use Array.from() on the iterator and extract the segmented graphemes, which can be reversed with .reverse(). Lastly, we join the array back into a string using .join()
There is also another option which you can try that has better browser support than Intl.Segmenter, however isn't as bullet-proof:
const reverse = str => Array.from(str.normalize('NFC')).reverse().join('');
this helps deal with characters consisting of multiple code points and code units. As pointed out in other answers, there are issues with maintaining composite and surrogate pair ordering in strings such as 'foo bar mañana mañana'. Here is a surrogate pair consisting of two code units, and the last ñ is a composite pair consisting of two Unicode characters to make up one grapheme (n+̃ = ñ).
In order to reverse each character, you can use the .reverse() method which is part of the Array prototype. As .reverse() is used on an array, the first thing to do is to turn the string into an array of characters. Typically, .split('') is used for this task, however, this splits up surrogate pairs which are made from up of multiple code units (as already shown in previous answers):
>> ''.split('')
>> `["�", "�"]`
Instead, if you invoke the String.prototype's Symbol.iterator method then you'll be able to retain your surrogate pairs within your array, as this iterates over the code points rather than the code units of your string:
>> [...'']
>> [""]
The next thing to handle is any composite characters within the string. Characters that consist of two or more code points will still be split when iterated on:
>> [...'ö']
>> ["o", "̈"]
The above separates the base character (o) from the diaresis, which is not desired behavior. This is because ö is a decomposed version of the character, consisting of multiple code points. To deal with this, you can use a string method introduced in ES6 known as String.prototype.normalize(). This method can compose multiple code points into its composed canonical form by using "NFC" as an argument. This allows us to convert the decomposed character ö (o + combining diaeresis) into its precomposed form ö (latin small letter o with diaeresis) which consists of only one code point. Calling .normalize() with "NFC" thus tries to replace multiple code points with single code points where possible. This allows graphemes consisting of two code points to be represented with one code point.
>> [...'ö'.normalize('NFC')]
>> ["ö"]
As normalize('NFC') produces one character, it can then be reversed safely when amongst others. Putting both the spread syntax and normalization together, you can successfully reverse strings of characters such as:
const reverse = str => Array.from(str.normalize('NFC')).reverse().join('');
console.log(reverse('foo bar mañana mañana'));
console.log(reverse('This emoji is happy'));
There are a few cases where the above normalization+iteration will fail. For instance, the character ❤️ (heavy black heart ❤️) consists of two code points. The first being the heart and the latter being the variation selector-16 (U+FE0F) which is used to define a glyph variant for the preceding character. Other characters can also produce similar issues like this.
Another thing to look out for is ZWJ (Zero-width joiner) characters, which you can find in some scripts, including emoji. For example the emoji comprises of the Man, Woman and Boy emoji, each separated by a ZWJ. The above normalization + iteration method will not account for this either.
As a result, using Intl.Segmenter is the better choice over these two approaches. Currently, Chrome also has its own specific segmentation API known as Intl.v8BreakIterator. This segmentation API is nonstandard and something that Chrome simply just implements. So, it is subject to change and doesn't work on most browsers, so it's not recommended to use. However, if you're curious, this is how it could be done:
const reverse = str => {
const iterator = Intl.v8BreakIterator(['en'], {type: 'character'});
iterator.adoptText(str);
const arr = [];
let pos = iterator.first();
while (pos !== -1) {
const current = iterator.current();
const nextPos = iterator.next();
if (nextPos === -1) break;
const slice = str.slice(current, nextPos);
arr.unshift(slice);
}
return arr.join("");
}
console.log(reverse('foo bar mañana mañana')); // anañam anañam rab oof
console.log(reverse('This emoji is happy')); // yppah si ijome sihT
console.log(reverse('Text surrogate pair composite pair möo varient selector ❤️ & ZWJ ')); // JWZ & ❤️ rotceles tneirav oöm riap etisopmoc riap etagorrus txeT
You can't reverse a string in place but you can use this:
String.prototype.reverse = function() {
return this.split("").reverse().join("");
}
var s = "ABCD";
s = s.reverse();
console.log(s);
function reverseString(string) {
var reversedString = "";
var stringLength = string.length - 1;
for (var i = stringLength; i >= 0; i--) {
reversedString += string[i];
}
return reversedString;
}
Reverse a String using built-in functions
function reverse(str) {
// Use the split() method to return a new array
// Use the reverse() method to reverse the new created array
// Use the join() method to join all elements of the array into a string
return str.split("").reverse().join("");
}
console.log(reverse('hello'));
Reverse a String without the helpers
function reversedOf(str) {
let newStr = '';
for (let char of str) {
newStr = char + newStr
// 1st round: "h" + "" = h, 2nd round: "e" + "h" = "eh" ... etc.
// console.log(newStr);
}
return newStr;
}
console.log(reversedOf('hello'));
ES6
function reverseString(str) {
return [...str].reverse().join("");
}
console.log(reverseString("Hello")); // olleH
Here are the four most common methods you can use to achieve a string reversal
Given a string, return a new string with the reversed order of characters
Multiple Solutions to the problem
//reverse('apple') === 'leppa'
//reverse('hello') === 'olleh'
//reverse('Greetings!') === '!sgniteerG'
// 1. First method without using reverse function and negative for loop
function reverseFirst(str) {
if(str !== '' || str !==undefined || str !== null) {
const reversedStr = [];
for(var i=str.length; i>-1; i--) {
reversedStr.push(str[i]);
}
return reversedStr.join("").toString();
}
}
// 2. Second method using the reverse function
function reverseSecond(str) {
return str.split('').reverse().join('');
}
// 3. Third method using the positive for loop
function reverseThird(str){
const reversedStr = [];
for(i=0; i<str.length;i++) {
reversedStr.push(str[str.length-1-i])
}
return reversedStr.join('').toString();
}
// 4. using the modified for loop ES6
function reverseForth(str) {
const reversedStr = [];
for(let character of str) {
reversedStr = character + reversedStr;
}
return reversedStr;
}
// 5. Using Reduce function
function reverse(str) {
return str.split('').reduce((reversed, character) => {
return character + reversed;
}, '');
}
use this method if you are more curious about performance and time complexity. in this method i have divided string in two part and sort it in length/2 times loop iteration.
let str = "abcdefghijklmnopqrstuvwxyz"
function reverse(str){
let store = ""
let store2 = ""
for(let i=str.length/2;i>=0;i--){
if(str.length%2!==0){
store += str.charAt(i)
store2 += str.slice((str.length/2)+1, str.length).charAt(i)
}else{
store += str.charAt(i-1)
store2 += str.slice((str.length/2), str.length).charAt(i)
}
}
return store2+store
}
console.log(reverse(str))
it is not optimal but we can think like this.
You could try something like this. I'm sure there's some room for refactoring. I couldn't get around using the split function. Maybe someone knows of a way to do it without split.
Code to set up, can put this in your .js library
Code to use it (has client side code, only because it was tested in a browser):
var sentence = "My Stack is Overflowing."
document.write(sentence.reverseLetters() + '<br />');
document.write(sentence.reverseWords() + '<br />');
Snippet:
String.prototype.aggregate = function(vals, aggregateFunction) {
var temp = '';
for (var i = vals.length - 1; i >= 0; i--) {
temp = aggregateFunction(vals[i], temp);
}
return temp;
}
String.prototype.reverseLetters = function() {
return this.aggregate(this.split(''),
function(current, word) {
return word + current;
})
}
String.prototype.reverseWords = function() {
return this.aggregate(this.split(' '),
function(current, word) {
return word + ' ' + current;
})
}
var sentence = "My Stack is Overflowing."
document.write(sentence.reverseLetters() + '<br />');
document.write(sentence.reverseWords() + '<br />');
Another variation (does it work with IE?):
String.prototype.reverse = function() {
for (i=1,s=""; i<=this.length; s+=this.substr(-i++,1)) {}
return s;
}
EDIT:
This is without the use of built-in functions:
String.prototype.reverse = function() {
for (i=this[-1],s=""; i>=0; s+=this[i--]) {}
return s;
}
Note: this[-1] holds a length of the string.
However it's not possible to reverse the string in place, since the assignment to individual array elements doesn't work with String object (protected?). I.e. you can do assigns, but the resulting string doesn't change.
No built-in methods? Given that strings in Javascript are immutable, you probably want to use the in-built methods like split, join, and so on. But here are two ways to go without those methods:
function ReverseString(str) {
var len = str.length;
var newString = [];
while (len--) {
newString.push(str[len]);
}
return newString.join('');
}
console.log(ReverseString('amgod')) //dogma
function RecursiveStringReverse(str, len) {
if (len === undefined)
len = str.length - 1;
if (len > 0)
return str[len] + RecursiveReverse(str, --len);
return str[len];
}
console.log(RecursiveStringReverse('Hello, world!'))// !dlrow ,olleH
Added for reverse of string without loop, it's working through recursion.
function reverse(y){
if(y.length==1 || y.length == 0 ){
return y;
}
return y.split('')[y.length - 1]+ reverse(y.slice(0, y.length-1));
}
console.log(reverse("Hello"));
function reverse(string)
{
let arr = [];
for(let char of string) {
arr.unshift(char);
}
let rev = arr.join('')
return rev
}
let result = reverse("hello")
console.log(result)
We can iterate the string array from both the ends: start and end, and swap at each iteration.
function reverse(str) {
let strArray = str.split("");
let start = 0;
let end = strArray.length - 1;
while(start <= end) {
let temp = strArray[start];
strArray[start] = strArray[end];
strArray[end] = temp;
start++;
end--;
}
return strArray.join("");
}
Although the number of operations have reduced, its time complexity is still O(n) as the number of operations still scale linearly with the size of input.
Ref: AlgoDaily
One of the way could also be using reduce method to reverse after using split method.
function reverse(str) {
return str.split('').reduce((rev, currentChar) => currentChar + rev, '');
}
console.log(reverse('apple'));
console.log(reverse('hello'));
console.log(reverse('Greetings!'));
function reverseWords(str) {
// Go for it
const invertirPalabra = palabra => palabra.split('').reverse().join('')
return str.split(' ').map(invertirPalabra).join(' ')
// con split convierto string en array de palabras, le paso ' '
// que es que me lo separe por espacios
// luego invierto cada palabra...
// y luego con join las uno separando por espacios
}
// try this simple way
const reverseStr = (str) => {
let newStr = "";
for (let i = str.length - 1; i >= 0; i--) {
newStr += str[i];
}
return newStr;
}
console.log(reverseStr("ABCDEFGH")); //HGFEDCBA
//recursive implementation
function reverse(wrd) {
const str =wrd[0]
if(!wrd.length) {
return wrd
}
return reverse(wrd.slice(1)) + str
}