0

In JavaScript I have a simple function I used to reverse a string, but my trainer at work says that I can cut the processing time in half within my for loop. My trainer doesn't want me to use the split/reverse/join functions. So that is why I am doing this in a for loop.

At first I thought using the charAT() function was the best way to achieve this, then I switched to just using brackets + the index position.

Currently scratching my head on how to make this more optimal and am wondering if it is maybe not a problem of the contents of the for loop, but maybe how I structured it in the first place?

function reversalFunct(initString) {

   let reverseString = '';

    for (let i = initString.length - 1; i >= 0; i--){
        reverseString += initString[i];
       
    }
    

    return reverseString;
  
}
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
mfoehrer
  • 35
  • 8
  • 3
    I would ask your 'trainer' what the hell they're thinking, lol. Maybe they're thinking you should build up an array rather than a string, and then join in in the final step? But I have doubts that would halve the runtime cost. And Stackoverflow can't guess what it's someone else's head – Andy Ray Jul 11 '23 at 21:56
  • "My trainer doesn't want me to use the split/reverse/join functions." Is that a hint to use `reduce`? Your code is fine and the javascript built-in functions are also O(n) (I think) if speed is a concern. Not sure what your trainer is getting at – fordat Jul 11 '23 at 21:59
  • `reduce` is almost always slower, more memory intensive, and harder to read than a loop. So hopefully that's not what their trainer is hinting at – Mark Hanna Jul 11 '23 at 22:03
  • 1
    Your trainer is wrong. Sure, you can change the code to loop over only half of the string and swap characters between first and second half, but that won't be any faster and also require you to use an array instead of a string. – Bergi Jul 12 '23 at 00:22

2 Answers2

1

Even within the training context, an array based collector for aggregating the reversed string data does not contradict the algorithm, the trainer/teacher might have in mind.

Thus, create a result array of same length of string and initialize a counter value of rounded half the length of the string's length. Count down this value to zero while separately incrementing a left side index starting from -1 and decrementing a right side index starting from the string's length.

Then assign the string's right index related character to the array's left index position and the string's left index related character to the arrays right index position. The iteration steps have been cut to half and the result array gets returned as its own joined string value representation.

function reverseString(value) {
  value = String(value);

  let idxLeft = -1;
  let idxRight = value.length;

  let count = Math.ceil(idxRight / 2);
  let result = new Array(idxRight);

  while (count--) {
    result[++idxLeft] = value[--idxRight];
    result[idxRight] = value[idxLeft];
  }
  return result.join('');
}

console.log(
  'Hello World =>', reverseString('Hello World')
);
console.log(
  'Anna Otto =>', reverseString('Anna Otto')
);

console.log(
  'null =>', reverseString(null)
);
console.log(
  'undefined =>', reverseString()
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

Taking into account Bergi's comment ...

"OP wrote that their trainer doesn't want them to use .join() though…"

... a reverse functionality based on (half) a loop, swapping and just string concatenation could be implemented like follows ...

function reverseString(value) {
  value = String(value);

  const strLength = value.length;
  const isEvenLength = strLength % 2 === 0;

  const halfWayIndex = Math.floor(strLength / 2);
  const halfWayOffset = isEvenLength && 1 || 0;

  let result = !isEvenLength && value[halfWayIndex] || '';

  for (let idx = 1; idx <= halfWayIndex; ++idx) {
    result =
      value[halfWayIndex - halfWayOffset + idx] +
      result +
      value[halfWayIndex - idx];
  }
  return result;
}

console.log(
  `'Hello World' => '${ reverseString('Hello World')}'`
);
console.log(
  `'Anna Otto' => '${ reverseString('Anna Otto')}'`
);

console.log(
  `null => '${ reverseString(null)}'`
);
console.log(
  `undefined => '${ reverseString()}'`
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
  • interesting how does it differ from my array iterations? seems the same? – Alexander Nenashev Jul 11 '23 at 22:43
  • you are wrong. i iterate the same half, look closer. your solution is the same as mine – Alexander Nenashev Jul 11 '23 at 23:01
  • @AlexanderNenashev ... you're right, I did not realize it at first glance. It's elegant. – Peter Seliger Jul 11 '23 at 23:07
  • thanks, mate, always glad to learn from you – Alexander Nenashev Jul 11 '23 at 23:09
  • OP wrote that their trainer doesn't want them to use `.join()` though… – Bergi Jul 12 '23 at 00:26
  • While this might be the algorithm that the trainer had thought of, I doubt it actually improves the processing time. Did you benchmark it? – Bergi Jul 12 '23 at 00:34
  • @Bergi ... nope, no benchmarks. I just assumed the trainer/teacher wanted an algorithm which swaps 2 characters from either end at once. I could have used string concatenation as well instead of assigning to a certain array position; but the swapping then would have been not anymore that obvious. And string concatenation would slow the process down even more, wouldn't it? – Peter Seliger Jul 12 '23 at 13:53
  • Yes, that's exactly what I'd assume, but I haven't benchmarked it either – Bergi Jul 12 '23 at 15:16
  • @Bergi ... I just finished updating _Alexander Nenashev's_ benchmark test. Though its hidden for some users others like you can run it. – Peter Seliger Jul 12 '23 at 15:22
  • @PeterSeliger Thanks. For me, the OP's "string concat" code is generally the fastest for normal-sized strings. Only from 10e6 characters upwards, the array versions become faster. – Bergi Jul 12 '23 at 15:40
  • @Bergi ... I'm not surprised at all about the OP's fully iterating and concatenating approach being (among) the fastest. Already yesterday it was clear that the teacher is theorizing and does not know about which technique actually contributes to "fast" and which even contradicts an initially smart thought like cutting the iteration steps two half. For me the question from the beginning was about "algorithm" in terms of the teacher and not about "actual performance". – Peter Seliger Jul 12 '23 at 16:13
1

You can think of the inversion problem as mirroring the string.

The first letter of the input string should end up at the last position in the result. In the same way, the last letter of the input string should end up at the first position in the result.

Therefore, you can iterate your input string only until half of its length and build the result from the middle part adding to the left and to the right of that center as you go.

You will have to take into account that if the length of the string is odd you have to start the result string with the letter in the middle:

const text = 'hello world';

function invertString(str = '') {
    const idx = Math.floor(str.length / 2);
    let result = str.length % 2 === 0 ? '' : str[idx];

    for (let i = idx - 1; i >= 0; i--) {
        const prevIndex = i;
        const nextIndex = str.length - i - 1;

        result = str[nextIndex] + result + str[prevIndex];
    }

    return result;
}

const inverted = invertString(text);
console.log(inverted);

Annex

Just for the sake of performance insight and for completeness in the answer, here is a benchmark of this solution versus the solution where all the string is looped: https://jsben.ch/OHGSk.

Even for long strings, the looping over the whole string results in better performance.

mgarcia
  • 5,669
  • 3
  • 16
  • 35
  • While this might be the algorithm that the trainer had thought of, I doubt it actually improves the processing time. Did you benchmark it? – Bergi Jul 12 '23 at 00:34
  • My response was more a thought of academical exercise (what the trainer may have thought) than a try to improve performance on the already existing answers. Nevertheless, for the sake of completion, I can add benchmarks of this solution vs the alternatives just to see how each of them performs. – mgarcia Jul 12 '23 at 17:10