6
\$\begingroup\$

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);
\$\endgroup\$
  • \$\begingroup\$ I think you want to keep the word order, of a string, but reverse each individual word in the string? Yes, the result of your code is "i ma tpircsavaj ". \$\endgroup\$ – KIKO Software Apr 28 at 7:27
7
\$\begingroup\$

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.

\$\endgroup\$
  • 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 call reverseString("😃"), the result is wrong without reverseWords being involved at all. \$\endgroup\$ – Roland Illig Apr 28 at 8:13
  • 1
    \$\begingroup\$ With the on-screen keyboard of my tablet. \$\endgroup\$ – Roland Illig Apr 28 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 Apr 28 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 Apr 28 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 string const W = "\u{1F30F}\u{1F30E}\u{1F30D}" is 🌏🌎🌍 the following is true [...W].length === 3 && W.length === 6 && W.split("").length === 6 \$\endgroup\$ – Blindman67 Apr 28 at 15:52
5
\$\begingroup\$

Your function has two bugs

  1. 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.

  2. 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 a var it should be declared at the top of the function or use for (let i. You can also use const in for...of and for...in loops. Eg for (const i of foo) {

  • Don't scrunch up the code. Space between ) {, for (, if (, } else and else {. Also operators should be separated by space. i = strlen-1 should be i = 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 = [];
  • 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 as words or sentence
    • strLen better as just length or charCount or even just len
    • reverseStr better as reversed
    • reverseStrArr maybe reversedWords

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

  1. 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.

  1. 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

\$\endgroup\$
  • \$\begingroup\$ Nice answer. I'll upvote, and we'll see what happens. \$\endgroup\$ – KIKO Software Apr 28 at 18:30
1
\$\begingroup\$

:-) 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(' ')) ;`
\$\endgroup\$
  • 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\$ – MEE was the missing bracket Apr 28 at 17:56
  • \$\begingroup\$ @MEEthesetupwizard map isn't explicitly mentioned so it may be allowed. split is out though. \$\endgroup\$ – JollyJoker Apr 29 at 7:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.