I want reverse the string with the same order. We should not use the Array functions like split(), .reverse()
and some other array functions also except array.length
.
Here i am attached my code. How to achieve that thing more efficiently.
var str = "i am javascript";
// result "i ma tpircsavaj"
function reverseString(myStr){
var strlen = myStr.length, result = "", reverseStr = "", reverseStrArr = [];
for(var i = strlen-1; i >= 0; i--){
reverseStr += myStr[i];
}
for(var j = 0; j < strlen; j++){
if(reverseStr[j] == " "){
reverseStrArr.push(result);
result = "";
}else{
result += reverseStr[j];
if(j + 1 == strlen){
reverseStrArr.push(result);
result = "";
}
}
}
for(var k=reverseStrArr.length - 1; k >= 0; k--){
result += reverseStrArr[k] + " "
}
console.log(result);
}
reverseString(str);
4 Answers 4
I would split this into two function, simply because they do different things:
First a simple string reverser:
function reverseString(input)
{
var output = "";
for(var i = input.length - 1; i >= 0; i--) {
output += input[i];
}
return output;
}
This is an easy function that everybody can understand. But we need to reverse words, so we make another function for that.
function reverseWords(input)
{
var word = "", output = "";
for(var i = 0; i < input.length; i++) {
if (input[i] == " ") {
output += reverseString(word) + " ";
word = "";
}
else {
word += input[i];
}
}
return output + reverseString(word);
}
And that's it. Now this might not be the niftiest, or shortest, solution, but it is one that reasonably easy to understand, and you get two functions for the price of one.
I had to correct this code after a comment from Blindman67 saying it didn't work for some cases.
A shorter version is also possible:
function reverseWordsShorter(input)
{
var word = "", output = "";
for(var i = input.length - 1; i >= 0; i--) {
if (input[i] == " ") {
output = " " + word + output;
word = "";
}
else {
word += input[i];
}
}
return word + output;
}
Also notice that there's no erroneous space at the end anymore.
-
4\$\begingroup\$ And in two months, when someone discovers that emojis are destroyed when they are reversed, you only have a single small function that you need to repair instead of a large function. That's because the bug is clearly in the
reverseString
function since when you callreverseString("π")
, the result is wrong withoutreverseWords
being involved at all. \$\endgroup\$Roland Illig– Roland Illig2019εΉ΄04ζ28ζ₯ 08:13:38 +00:00Commented Apr 28, 2019 at 8:13 -
1\$\begingroup\$ With the on-screen keyboard of my tablet. \$\endgroup\$Roland Illig– Roland Illig2019εΉ΄04ζ28ζ₯ 08:29:40 +00:00Commented Apr 28, 2019 at 8:29
-
2\$\begingroup\$ And then, one month later, another pedantic comes along and says that
"AA\u0308A"
looks like"AÄA"
, but its reversed version puts the dots on one of the outer characters. And there you go, implementing all the trickery in reversing a string. The Swift programming language has these built-in, by the way. Many other programming languages lack decent string reversing support, simply because it's complicated and not needed often. \$\endgroup\$Roland Illig– Roland Illig2019εΉ΄04ζ28ζ₯ 08:57:52 +00:00Commented Apr 28, 2019 at 8:57 -
1\$\begingroup\$ @Graipher I'd say Python 3 is as naive as almost all other programming languages:
print("".join(reversed("AA\u0308A")))
puts the umlaut dots above the first A instead of the middle. \$\endgroup\$Roland Illig– Roland Illig2019εΉ΄04ζ28ζ₯ 14:00:05 +00:00Commented Apr 28, 2019 at 14:00 -
2\$\begingroup\$ Your answer returns reversed word positions with words unchanged. eg For
"cat on mat"
you return"mat on cat"
.Should be"tac no tam"
. Also use String iterator to handle unicode codepoints as single items. eg with stringconst W = "\u{1F30F}\u{1F30E}\u{1F30D}"
is πππ the following istrue
[...W].length === 3 && W.length === 6 && W.split("").length === 6
\$\endgroup\$Blindman67– Blindman672019εΉ΄04ζ28ζ₯ 15:52:34 +00:00Commented Apr 28, 2019 at 15:52
Your function has two bugs
Adds an extra space to the end of the string. Eg for
"the cat sat on the mat"
you return"eht tac tas no eht tam "
. The input string is 22 characters long and the returned string is 23 characters long.Related to the first. If the input string starts with a space the returned string removes it. Eg for
" the cat sat on the mat"
you return "eht tac tas no eht tam ".
Code style
This section will cover source code style. These points are independent of the algorithms logic.
You are missing the semicolon for the line inside the last
for
statement.Use
===
or!==
rather than==
or!=
You should never see
for (var i
If you use avar
it should be declared at the top of the function or usefor (let i
. You can also useconst
infor...of
andfor...in
loops. Egfor (const i of foo) {
Don't scrunch up the code. Space between
) {
,for (
,if (
,} else
andelse {
. Also operators should be separated by space.i = strlen-1
should bei = strlen - 1
Use
const
for variables that do not change.- The variable
strlen
does not change, thus it should be a constant. - The variable
reverseStrArr
holds a reference to an array. That reference never changes so the variable should be declared as a constant.const reverseStrArr = [];
- The variable
You should try to avoid adding a variable's type to its name.
Names need only fit the context (have semantic meaning) of the scope/function they are declared in.
myStr
better aswords
orsentence
strLen
better as justlength
orcharCount
or even justlen
reverseStr
better asreversed
reverseStrArr
maybereversedWords
Loop variables
You don't need to declare a different variable of each loop. You need only do that if the loops are nested.
// Nested
var i,j;
for (i = 0; i < 10; i++) {
for (j = 0; j < 10; j++) { /*...*/ }
}
//or
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 10; j++) { /*...*/ }
}
// Sequential
var i;
for (i = 0; i < 10; i++) { /*...*/ }
for (i = 0; i < 10; i++) { /*...*/ }
//or
for (let i = 0; i < 10; i++) { /*...*/ }
for (let i = 0; i < 10; i++) { /*...*/ }
Algorithms Logic
This section looks at how your function solves the problem and possible ways to improve it.
Array as a stack
You are using the array reverseStrArr
as a stack. An array becomes a stack only by the way you use it, it is an abstracted stack.
Stacks are first in last out. You add items one at a time to the top of the stack, then you remove items one at a time from the top.
The two array function push()
and pop()
provide a fast way to use JS arrays as a stack.
As a stack the final loop where you create the result
string can be simplified as
while (reversedWords.length) {
result += " " + reversedWords.pop();
}
(I think the rules will let us use pop
.)
Unneeded steps
- The Last Word
In the second loop you have the statement if(j + 1 == strlen) {
to push the last result
to the array, then you clear result
.
If you remove that statement and its content you can have result
hold the last word. In the following loop result
already has the first word so you can add the space then word rather than word an space (trailing space bug)
This fixes both bugs.
- Repeated Logic
The first loop goes from last to first creating a reversed string, then the next loop indexes the string created from first to last. Its like writing backward so the when you read forward its backward... LOL
The first loop is not needed. Just read the sentence string backward as you process it.
Rewrite
Putting all the above into your function we get a much simpler source and fix the bugs at the same time.
function reverseString(words) {
const reversed = [], len = words.length;
var result = "", i = len;
while (i-- > 0) {
if (words[i] == " ") {
reversed.push(result);
result = "";
} else {
result += words[i];
}
}
while (reversed.length) {
result += " " + reversed.pop();
}
return result;
}
Alternative
The rewrite is still a little too complex .
I used two loops to first create a stack of words and then use the stack to rebuild the sentence.
You can do both within the one loop removing the need to use a stack.
function reverseWords(sentence) {
var word = "", reversed = "", i = sentence.length;
while (i-- > 0) {
if (sentence[i] === " ") {
reversed = " " + word + reversed;
word = "";
} else { word += sentence[i] }
}
return word + reversed;
}
Surrogates, Codepoints, Emoji, modifiers, sequences π²πππ
I see that in the accepted answer there are comments regarding Emoji (surrogate Unicode characters)
In JavaScript strings are iteratable. The iterator handles all codepoint characters as single items.
Note: Single emoji can be created from one or more codepoints. eg π©π»ββ€βπ§π» is one emoji (on Chrome win10) but contains 6 codepoints or 8 Javascript string Unicode characters. See note at bottom of answer.
Using iterators to step over strings and you need not worry about character sequences.
Unfortunately iterators don't run backward. As we can not use many of the Array functions we are limited in how to solve the problem
Simplest solution.
To solve the problem of surrogate characters (within the restriction of the question) we can use a very quick hack that converts the input string to an array.
sentence = [...sentence]; // separates codepoints
The rest of the function remains unchanged because we used bracket notation to index the strings.
Example
Shows pass and failure modes when reversing surrogates via string iterator.
function reverseWords(sentence) {
sentence = [...sentence];
var word = "", reversed = "", i = sentence.length;
while (i-- > 0) {
if (sentence[i] === " ") {
reversed = " " + word + reversed;
word = "";
} else { word += sentence[i] }
}
return word + reversed;
}
const test1 = "π± The cat π© on the mat π²πππ";
const test2 = `π±ββοΈπ²πΌπ¨π½π±πΎββοΈπ¦πΏ π©π»\u200Dβ€\u200Dπ§π» \u{1F3C4}\u200D\u2642\uFE0F`;
log(test1);
log("Reverse...");
log(reverseWords(test1));
log("--------------------------------------");
log("Emoji modifiers and sequences may fail.");
log(`${test2} Reversed is...`);
log(`${reverseWords(test2)}`);
function log(textContent) {
info.appendChild(Object.assign(document.createElement("div"),{textContent}));
}
#info {
font-size: large;
}
<code id="info"><code>
Note: Emoji modifiers and sequences rely not only on how Javascript handles the string but also how the device OS interprets the string.
Use String.prototype.codePointAt()
to locate modifiers, sequences, gruping, etc
For a full Emoji rundown Unicode Emoji Technical Standard
-
\$\begingroup\$ Nice answer. I'll upvote, and we'll see what happens. \$\endgroup\$KIKO Software– KIKO Software2019εΉ΄04ζ28ζ₯ 18:30:37 +00:00Commented Apr 28, 2019 at 18:30
:-) Hello kallis,
Could you please clarify why you don't want to put your string in an array ?
Indeed, you could use a call back function with a map method on an array of words. The callback function would reverse each word, while keeping them at the same place in the sentence.
let str = "I am Javascript" ;
const arrStr = (string) => {
const string2 = string.split(' ') ;
return string2 ;
}
let strInArray = arrStr(str) ;
strInArray = strInArray . map ( word => {
let l = word.length ;
let output = '' ;
for (var i=l-1 ; i>=0 ; i--) {
output += word[i] ;
}
return output ;
} // End of callback function
) ; // End of map method
console.log(strInArray.join(' ')) ;`
-
2\$\begingroup\$ Hello SylviE, welcome to Code Review Stack Exchange. The asker said, that most array functions are forbidden, so they probably can't use
Array.map()
. Also your choice of whitespace looks a bit odd. (Space around.
and before;
for example). \$\endgroup\$User that hates AI– User that hates AI2019εΉ΄04ζ28ζ₯ 17:56:05 +00:00Commented Apr 28, 2019 at 17:56 -
\$\begingroup\$ @MEEthesetupwizard
map
isn't explicitly mentioned so it may be allowed.split
is out though. \$\endgroup\$JollyJoker– JollyJoker2019εΉ΄04ζ29ζ₯ 07:55:48 +00:00Commented Apr 29, 2019 at 7:55
Generally in an interview setting, if you're asked this question, you'll be asked to find a solution by modifying the string in place.
In this case, make sure to split out your code into separate functions, to make it easier for you to reason about.
function replaceAt(str, index, replacement) {
return str.slice(0, index) + replacement + str.slice(index + 1)
}
function insertAt(str, start, end, value) {
return str.slice(0, start) + value + str.slice(end + 1)
}
function swap(str, left, right) {
let temp = str[right]
str = replaceAt(str, right, str[left])
str = replaceAt(str, left, temp)
return str
}
function reverseString(str) {
for (let i = 0; i < str.length / 2; i++) {
str = swap(str, i, str.length - 1 - i)
}
return str
}
function reverseWords(str) {
str = reverseString(str)
let prev = 0
let temp = ''
for (let i = 0; i < str.length; i++) {
if (str[i] === ' ') {
str = insertAt(str, prev, i - 1, reverseString(temp))
prev = i + 1
temp = ''
} else {
temp += str[i]
}
}
if (temp.length) {
str = insertAt(str, prev, str.length - 1, reverseString(temp))
}
return str
}
-
\$\begingroup\$ It also happens to be a better coding practice. \$\endgroup\$2020εΉ΄02ζ26ζ₯ 15:41:15 +00:00Commented Feb 26, 2020 at 15:41
"i ma tpircsavaj "
. \$\endgroup\$