Reverse a string without using reversed() or [::-1]?

I came across a strange Codecademy exercise that required a function that would take a string as input and return it in reverse order. The only problem was you could not use the reversed method or the common answer here on stackoverflow, [::-1].

Obviously in the real world of programming, one would most likely go with the extended slice method, or even using the reversed function but perhaps there is some case where this would not work?

I present a solution below in Q&A style, in case it is helpful for people in the future.


You can also do it with recursion:

def reverse(text):
    if len(text) <= 1:
        return text

    return reverse(text[1:]) + text[0]

And a simple example for the string hello:

   reverse(hello)
 = reverse(ello) + h           # The recursive step
 = reverse(llo) + e + h
 = reverse(lo) + l + e + h
 = reverse(o) + l + l + e + h  # Base case
 = o + l + l + e + h
 = olleh

Just another option:

from collections import deque
def reverse(iterable):
    d = deque()
    d.extendleft(iterable)
    return ''.join(d)

Use reversed range:

def reverse(strs):
    for i in xrange(len(strs)-1, -1, -1):
        yield strs[i]
...         
>>> ''.join(reverse('hello'))
'olleh'

xrange or range with -1 step would return items in reversed order, so we need to iterate from len(string)-1 to -1(exclusive) and fetch items from the string one by one.

>>> list(xrange(len(strs) -1, -1 , -1))
[4, 3, 2, 1, 0]  #iterate over these indexes and fetch the items from the string

One-liner:

def reverse(strs):
    return ''.join([strs[i] for i in xrange(len(strs)-1, -1, -1)])
... 
>>> reverse('hello')
'olleh'

EDIT

Recent activity on this question caused me to look back and change my solution to a quick one-liner using a generator:

rev = ''.join([text[len(text) - count] for count in xrange(1,len(text)+1)])

Although obviously there are some better answers here like a negative step in the range or xrange function. The following is my original solution:


Here is my solution, I'll explain it step by step

def reverse(text):

    lst = []
    count = 1

    for i in range(0,len(text)):

        lst.append(text[len(text)-count])
        count += 1

    lst = ''.join(lst)
    return lst

print reverse('hello')

First, we have to pass a parameter to the function, in this case text.

Next, I set an empty list, named lst to use later. (I actually didn't know I'd need the list until I got to the for loop, you'll see why it's necessary in a second.)

The count variable will make sense once I get into the for loop

So let's take a look at a basic version of what we are trying to accomplish:

It makes sense that appending the last character to the list would start the reverse order. For example:

>>lst = []
>>word = 'foo'
>>lst.append(word[2])
>>print lst
['o']

But in order to continue reversing the order, we need to then append word[1] and then word[0]:

>>lst.append(word[2])
>>lst.append(word[1])
>>lst.append(word[0])
>>print lst
['o','o','f']

This is great, we now have a list that has our original word in reverse order and it can be converted back into a string by using .join(). But there's a problem. This works for the word foo, it even works for any word that has a length of 3 characters. But what about a word with 5 characters? Or 10 characters? Now it won't work. What if there was a way we could dynamically change the index we append so that any word will be returned in reverse order?

Enter for loop.

for i in range(0,len(text)):

    lst.append(text[len(text)-count])
    count += 1

First off, it is necessary to use in range() rather than just in, because we need to iterate through the characters in the word, but we also need to pull the index value of the word so that we change the order.

The first part of the body of our for loop should look familiar. Its very similar to

>>lst.append(word[..index..])

In fact, the base concept of it is exactly the same:

>>lst.append(text[..index..])

So what's all the stuff in the middle doing?

Well, we need to first append the index of the last letter to our list, which is the length of the word, text, -1. From now on we'll refer to it as l(t) -1

>>lst.append(text[len(text)-1])

That alone will always get the last letter of our word, and append it to lst, regardless of the length of the word. But now that we have the last letter, which is l(t) - 1, we need the second to last letter, which is l(t) - 2, and so on, until there are no more characters to append to the list. Remember our count variable from above? That will come in handy. By using a for loop, we can increment the value of count by 1 through each iteration, so that the value we subtract by increases, until the for loop has iterated through the entire word:

>>for i in range(0,len(text)):
..        
..      lst.append(text[len(text)-count])
..      count += 1

Now that we have the heart of our function, let's look at what we have so far:

def reverse(text):

    lst = []
    count = 1

    for i in range(0,len(text)):

        lst.append(text[len(text)-count])
        count += 1

We're almost done! Right now, if we were to call our function with the word 'hello', we would get a list that looks like:

['o','l','l','e','h']

We don't want a list, we want a string. We can use .join for that:

def reverse(text):

    lst = []
    count = 1

    for i in range(0,len(text)):

        lst.append(text[len(text)-count])
        count += 1

    lst = ''.join(lst) # join the letters together without a space
    return lst

And that's it. If we call the word 'hello' on reverse(), we'd get this:

>>print reverse('hello')
olleh

Obviously, this is way more code than is necessary in a real life situation. Using the reversed function or extended slice would be the optimal way to accomplish this task, but maybe there is some instance when it would not work, and you would need this. Either way, I figured I'd share it for anyone who would be interested.

If you guys have any other ideas, I'd love to hear them!