How can I get a reversed copy of a list (avoid a separate statement when chaining a method after .reverse)?

Question:

This code fails:

fCamel = 'F'
bCamel = 'B'
gap = ' '

k = ['F', ' ', 'B', 'F']

def solution(formation):
    return ((formation.index(bCamel) > (len(formation) - 1 - (formation.reverse()).index(fCamel))))

solution(k)

I get an exception that says AttributeError: 'NoneType' object has no attribute 'index'.

I know that the problem is that list.reverse() returns None, modifying the list in-place. I want to use .index on the reversed list. Is there a way I can avoid using a separate statement to reverse the list before indexing into it? How?

Asked By: nakiya

||

Answers:

You can use reversed(formation) to return a reverse iterator of formation. When you call formation.reverse() it does an in place reversal of the list and returns None.

EDIT:

I see what you are trying to do now, in my opinion it’s easier to just do this with a list comprehension:

def solution(formation):
    return len([k for k in formation[formation.index(bCamel)+1:] if k == fCamel]) == 0

This basically looks at all the elements after the first bCamel and collects all the elements that have the value fCamel. If that list has a length == 0 you have a solution.

Here’s a few examples:

>>> k = ['F','F','B','B','F']
>>> solution(k)
False
>>> k = ['F','F','B','B','B']
>>> solution(k)
True
>>> k = ['F','F','B','F','F','B','B']
>>> solution(k)
False
>>> 
Answered By: GWW

list.reverse reverses inplace. That is:

>>> l = [1, 2, 3]
>>> l.reverse()
>>> l
[3, 2, 1]

Please consult the Python documentation, things like these are laid out there. You can also try the ‘help’ built-in:

help(l.reverse) Help on built-in function reverse:

reverse(…)
L.reverse() — reverse IN PLACE

Answered By: rbp

To build on GWW’s answer, if you want this code to work as is you would just do list(reversed(formation)). If you really want to be able to use formation.reverse() instead, you would have to subclass list:

>>> class ReversableList(list):
...     def reverse(self):
...         return list(reversed(self))
... 
>>> x = ReversableList([1,2,3])
>>> x.reverse()
[3, 2, 1]

Whether or not this is advisable is another question of course.

Answered By: Jason Baker

Not super beautiful, but I did not find the equivalent in the precedent answers. If the speed or memory costs are low (the list is not very long or the operation not repeated a huge amount of times), this is pretty straight forward and even easier to read.

import copy

fCamel = 'F'
bCamel = 'B'
gap = ' '

k = ['F', ' ', 'B', 'F']

def solution(formation):
    rev_formation = copy.copy(formation)
    rev_formation.reverse()
    return ((formation.index(bCamel) > (len(formation) - 1 -
            (rev_formation).index(fCamel))))

Cheers

Answered By: Morlock

You can use slicing to return the reversed list:

l[::-1]
Answered By: Leon

I just came across this problem and wanted to clarify some things for users new to python coming from a javascript background.

In javascript, a.reverse() reverses in place and also returns the array when called.

Javascript:

var a = [2, 3 ,4]
console.log(a.reverse())
// outputs [4, 3, 2]
console.log(a)
// outputs [4, 3, 2]

In python, a.reverse() reverses in place, but does not return the array. This is what caused confusion for me.

In python:

a = [2, 3, 4]
a.reverse()
print(a)
# outputs [4, 3, 2]
# can't do print(a.reverse())
Answered By: Cisco G

This doesn’t provide a solution to the F _ B F pattern problem, but it does address the issue of python not returning a list when you use .reverse().

This is how I got around it:

chars = ['a', '-', 'c']
chars2 = [] + chars
chars2.reverse()
totalChars = chars + chars2

totalChars returns a-cc-a, which is what I wanted, AND chars2 is a list, not a pointer to chars. Hope this helps.

Answered By: Alison Stuart

the following changes will be effective:

NumSet={1,2,3,4,5,6,7,8,9,10}
NumList = list(NumSet)
NumList.reverse()
print(NumList)
  • avoid using the assignment operator after the initial assignment as lists are mutable types.

  • Using = operator with a method..(eg NumList = NumSet.reverse()) will cause the method to overwrite list with a blank, thereby effectively clearing the list. That’s why list becomes a NoneType.
    Methods are functions and doesn’t actually have its own value, thus the blank.

Answered By: Tyshc

I don’t know if this works for you, but this works for me:

list = [1,2,3]
print([list, list.reverse()][0])

The reason list.reverse() returns None is because the function doesn’t return anything.

Using your code:

fCamel = 'F'
bCamel = 'B'
gap = ' '

k = ['F', ' ', 'B', 'F']

def solution(formation):
    return ((formation.index(bCamel) > (len(formation) - 1 - ([formation, formation.reverse()][0]).index(fCamel))))

print(solution(k))

Hope this works for you!

ps. Honestly, I have no idea why this works. I stumbled on it accidentally.

Answered By: Abhiraam Eranti

Title question is already answered, but for what you were really after:

Basically, this function should return true if all the ‘F’s are on the left hand side of the first ‘B’

That’s the same as there’s no ‘B’ followed by an ‘F’. Nice way to check this is with an iterator:

def solution(formation):
    it = iter(formation)
    return not (bCamel in it and fCamel in it)

Some advantages:

  • Unlike every formation.index(...) solution it doesn’t crash if the searched value isn’t there.
  • Takes only O(1) extra space (unlike the solutions making a reversed copy of the list).
  • Touches every element at most once and stops as soon as possible. Even has O(1) time best case (even with millions of elements, if the list starts with ['B', 'F', then it stops right there).
Answered By: Stefan Pochmann

There are multiple approaches to the problem shown in the OP. A summary:

  • Use a different technique that gives you a reversed copy of the list, so that this expression can be used in place. The two main ways to do this are formation[::-1] and list(reversed(formation)) (see How to reverse a list?).

Analogous solutions exist to replace other list functionality, for example:

# mylist.append(1)
# mylist.index(1)
(mylist + [1]).index(1) # note that the value is wrapped in another list

# mylist.extend(anotherlist)
# mylist.index(1)
(mylist + anotherlist).index(1)

# mylist.sort()
# mylist.index(1)
sorted(mylist).index(1)
  • Bite the bullet and use a separate statement anyway. Simple is better than complex; good Python style often avoids long expressions like ((formation.index(bCamel) > (len(formation) - 1 - (formation.reverse()).index(fCamel)))) because it’s hard to follow the logic. Keep in mind that since we are still using an in-place method, the original formation is still modified. This can be useful, but it often causes problems.

  • (Please do not use this in real code.) We can abuse conditional expressions to make the assignment happen as a side effect:

def solution(formation):
    return formation.index(bCamel) > (
         len(formation) - 1 - (formation.reverse() or formation).index(fCamel)
    )

The idea is that since formation.reverse() will return None, which is falsey, so or is forced not to short-circuit, and will evaluate to formationafter the reversal has occurred as a side effect.

Other expressions can have the same net effect, e.g. [formation, formation.reverse()][0]. The idea is to write something that is an expression that includes the .reverse call, but evaluates to the original list object. We can be arbitrarily creative here – but again, Simple is better than complex. Please don’t do these things.

Again, keep in mind that this will still modify the original list, which may impact on future calculations.

  • Rework the logic to avoid the need to reverse the list. The code tries to reverse the list, search in the reversed list for the index of the first match, and then subtract that result from len(formation) - 1 – the overall effect of this is to search for the index of the last match. Lists don’t have a method for that, but strings do; and as it happens, all our elements are single-character strings. We could solve the problem more simply by representing the formation with a string:
def solution(formation):
    return formation.index(bCamel) > formation.rindex(fCamel)

solution('F BF')

Alternately, we can conceive of the problem differently: "Is there a fCamel in the part of the list that follows the first bCamel?" The accepted answer shows using a list comprehension to iterate over "the part of the list that follows the first bCamel", making a list of all the fCamels there, and checking whether that list is non-empty. But we can do it much more simply:

# works with either the string or list input
def solution(formation):
    return fCamel not in formation[formation.index(bCamel)+1:]

(There are even more clever ways to write this, such as Stefan’s answer using iterators.)

Solutions like this are specific to the problem solved by the code, and don’t answer the question in general. However, it is often possible to find similar solutions in other contexts.

Answered By: Karl Knechtel
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.