How to reverse the elements in a sublist? - python

I'm trying to create a function that reverses the order of the elements in a list, and also reverses the elements in a sublist. for example:
For example, if L = [[1, 2], [3, 4], [5, 6, 7]] then deep_reverse(L) mutates L to be [[7, 6, 5], [4, 3], [2, 1]]
I figured out how to reverse the order of one list, but I am having troubles with reversing the order of elements in a sublist. This is what I have so far:
def deep_reverse(L)
"""
assumes L is a list of lists whose elements are ints
Mutates L such that it reverses its elements and also
reverses the order of the int elements in every element of L.
It does not return anything.
"""
for i in reversed(L):
print(i)
In the example above, my code would just print [5,6,7], [3,4], [1,2], which is not what i'm trying to accomplish. It's just reversing the order of the lists, the not actual elements in the lists.
What should I add to the code so that it also reverses the order of the elements in a sublist?
[EDIT: my code needs to mutate the list; I don't want it just to print it, it actually needs to change the list.]

[sublist[::-1] for sublist in to_reverse[::-1]]
List comprehension works here. [::-1] is basically the same as reversed, but does not modify the list.
EDIT:
As pointed out below, reversed doesn't modify the list. It returns a listreverseiterator object
More Edit:
If you want a solution for lists of arbitrary depth, try:
def deep_reverse(to_reverse):
if isinstance(to_reverse, list):
return list(map(deep_reverse, to_reverse[::-1]))
else:
return to_reverse
Even more Edit:
To mutate a list in a function:
L[:] = new_list
Will modify the list in place.

I'm trying to create a function that reverses the order of the elements in a list, and also reverses the elements in a sublist.
Then do exactly those two things:
L.reverse()
for sublist in L:
sublist.reverse()
Full demo because you seem to be confused about what your function is supposed to do and how to test it:
>>> def deep_reverse(L):
"""
assumes L is a list of lists whose elements are ints
Mutates L such that it reverses its elements and also
reverses the order of the int elements in every element of L.
It does not return anything.
"""
L.reverse()
for sublist in L:
sublist.reverse()
>>> L = [[1, 2], [3, 4], [5, 6, 7]]
>>> deep_reverse(L)
>>> print(L)
[[7, 6, 5], [4, 3], [2, 1]]

Alternatively you use map() to achieve this as:
>>> map(lambda x: x[::-1], L[::-1]) # In Python 2.x
[[7, 6, 5], [4, 3], [2, 1]]
>>> list(map(lambda x: x[::-1], L[::-1])) # In Python 3.x
[[7, 6, 5], [4, 3], [2, 1]]
Check Blog on Lambda, filter, reduce and map to know how lambda functions and map() works in Python.

This should do the trick.
L = [[1, 2], [3, 4], [5, 6, 7]]
def deep_reverse(L):
for i in range(len(L)):
L[i]=L[i][::-1]
L=L[::-1]
return L

This looks very familiar :). I'm not going to give the whole working solution but here are some tips:
As you know, there are two steps, reverse each sub-list and then reverse the outer list (in place, without making a new list, so it will mutate the global L).
So you can loop through the outer list, and mutate each sub-list:
for i in range(len(L)):
# if L[i] is a list:
# reverse with [::-1] and update L[i] to the reversed version
# reverse the outer list L, list.reverse() will operate in-place on L
Now remember, if you loop through the list like this:
for item in list:
item = 'xxx'
You can't change item with the above code. item is a placeholder value, so changing it doesn't actually modify the list.
You instead need to index the item in L, and enumerate can help with this, or you can use the less-preffered range(len()) as above.
for i, item in enumerate(L):
# do something with L[i]
L[i] = 'something'
Edit: since there is so much confusion over this, I'll go ahead and post a working solution based on Stefan Pochmann's very elegant answer:
def deep_reverse(L):
L.reverse()
for sublist in L:
sublist.reverse()
Notice there is no return statement, and no print statement. This will correctly modify L in place. You cannot reassign L inside the function because then it will just create a new local version of L, and it will not modify the global L. You can use list.reverse() to modify L in place which is necessary based on the specifications.

with functional paradigm and defensive programming:
def deep_reverse(L):
""" assumes L is a list of lists whose elements are ints
Mutates L such that it reverses its elements and also
reverses the order of the int elements in every element of L.
It does not return anything.
"""
# Your code here
for i in L:
try:
deep_reverse(i)
except:
pass
L.reverse()

You could make this recursive, so it will work for arbitrarily deep nests.
something like (UNTESTED):
def deep_reverse(L)
"""
assumes L is a list of lists whose elements are ints
Mutates L such that it reverses its elements and also
reverses the order of the int elements in every element of L.
It does not return anything.
"""
for i in reversed(L):
if len(i) > 1:
deep_reverse(i)
else:
print(i)

Related

Collecting first item of a list in a list of lists

I have a list that looks like the following one:
my_list = [[[(2,3,4,5)]],[[8,2,4,2]],[[9,0,0,0]]]
Now, I want to find a way to make a new list zero_list whose elements will be the 0th entries of each element in my_list. That is
zero_list = [2, 8, 9]
How could I make a for loop for iterating the 0th element of a list whose elements are lists themselves? Of course in my real example I have a much bigger such list with thousands of entries who are lists themselves.
P.S. I understand this is probably an easy question but I could not figure it out.
For any depth list you can use this recursive function
def get_first(seq):
if isinstance(seq, (tuple, list)):
return get_first(seq[0])
return seq
def get_zero_list(seq):
return [get_first(i) for i in seq]
my_list = [[[[[[[[(2,3,4,5)]]]]]]],[[8,2,4,2]],[[9,0,0,0]]]
print(get_zero_list(my_list)) # [2, 8, 9]
my_list = [[[[[[[[(2,3,4,5)]]]]]]],[[[[[[[('first', 3)]]]], 2, 3],2,4,2]],[[([['a', 2]]),0,0,0]]]
print(get_zero_list(my_list)) # [2, 'first', 'a']
Zero_list = [ items[0] for sublist in my_list for items in sublist]

How to change the index in a list of lists

I would like to change the way a list of lists in indexed.
Suppose my initial list is two lists of one list and two lists of three elements. For example:
L = [[[1, 2, 3]], [[4, 5, 6], [7, 8, 9]]]
Then let say I want to take '4' in L, I must do
L[1][0][0].
Now I'd like to create a new list such that the last indexing become the first one
Lnew = [[[1], [4, 7]], [[2], [5, 8]], [[3], [6, 9]]]
And then for taking '4' I have to do:
Lnew[0][1][0]
In a more general case, I'd like to create the list Lnew defined by:
Lnew[i][k][l] = L[k][l][i]
Is there a way to do this kind of permutation of the index without doing the following loops:
Lnew = []
for i in range(len(Payment_dates)):
L1 = []
for k in range(N+1):
L2 = []
for l in range(k+1):
L2.append(L[k][l][i])
L1.append(L2)
Lnew.append(L1)
Which is not very optimal in term of complexity.
Thanks
What you'd like to achieve, presupposes that all sublists have the same length.
If they have differing lengths, you may wish to append zeros to all sublists until they have the length of the longest sublist or (which is easier) until infinity.
The same behaviour can be achieved by using a function to access the elements of the list. You can call this function during runtime every time you need an element of the list:
def getElement(myList, i, k, l):
if k < myList.length and l < myList[k].length and i < myList[k][l].length:
return myList[k][l][i]
else:
return None # or zero, or whatever you prefer
Depending on your code structure, you might not need this as a function and you can just put the conditions inside of your code.
You can also nest the if-conditions and throw different errors or return different values depending on what level the element does not exist.
If we neglect the time complexity of outputting a multidimensional list's element, this approach should decrease your time complexity from O(n^3) to O(1).

del statement not working for list

I have a long list where each element is a list of length 2. The first element of each is a list is a string and the second element of each list is an integer corresponding to the string.
I want to loop through the long "parent" list and delete any "child" lists where the integer is less than three. This is my code.
for i in range(len(fontsizenum) / 2):
if int(fontsizenum[i][1]) < 3:
del fontsizenum[i]
However, it is not working as when I print the list afterwards, it still contains values with numbers less than three.
Say this is the list that I am altering.
fontsizenum = [[cereal, 1], [dog, 4], [cat, 2], [water, 5]]
The expected output is [[dog, 4], [water, 5]].
However, the actual output for me right now is still the original, unchanged list.
The resulting list still contains unexpected values because you are modifying the list while iterating over it. In almost all cases, you should avoid modifying an iterable while iterating over it.
If you change your code to the following, the resulting list should be what you expect.
new_lst = []
for idx, value in enumerate(lst):
if value[1] >= 3:
new_lst.append(value)
As noted in the comments by #AntonvBR, the above snippet can be simplified to the following list comprehension
[i for i in lst if i[1] > 3]
Example
If lst is set to
[
['forbidden', 1],
['hath', 1],
['causes', 2],
['whose', 3],
]
then the resulting list will be
[['whose', 3]]
Explanation
My code snippet creates a new list that contains elements from the old list. Notably, this allows us to avoid modifying the old list.
I changed the condition in the if-statement to check whether to include, rather than to exclude, an element. Finally, if that check is satisfied, then I append the value to the new list.
You want to use a simple list comprehension for this:
expectedoutput = [i for i in fontsizenum if i[1] >= 3]

How to pick the smallest value in a list when an iterative process is applied to the list

I need to find the smallest value in a series of lists. I understand the code for the smallest value in just one list:
>>> x = [1, 2, 3, 4, 5]
>>> print (min(x))
1
Simple enough. However, I would like to know if there is a way to write code that finds the smallest value for each list I have without stopping and adjusting the code (whether by an iterative process or some other means). Any help would be greatly appreciated. Thanks in advance!
First, make a list of lists out of your separate lists. For example, if you have lists A, B and C, the list of lists would be [A,B,C].
Now, to get a list of all the minimum values for each list in a list of lists lst:
[min(x) for x in lst]
To get the global minimum:
min(x for sublist in lst for x in sublist)
Demo:
>>> lst
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> [min(x) for x in lst]
[1, 4, 7]
>>> min(x for sublist in lst for x in sublist)
1
Edit in reaction to OP's comment:
I just want the minimum value for each list. I don't need to compile all of the minimum values together into a new list
If you just want to print the minimum values for each list A, B, C, ... , you can do:
for lst in (A,B,C): # put as many lists as you like into the parentheses
print(min(lst))
Edit in reaction to
I am only accessing one list (in this case, the values are well depths) at a time. [..] What I would like to know is how to write a code that finds the smallest value in a list given that the iterator essentially changes the values in said list.
Just print(min(lst)) each time lst has changed.
Assuming you've got a list of lists, this should do it:
minimums = [min(l) for l in lists]

Python index usage

Here is some code:
# Question 9: Deep Reverse
# Define a procedure, deep_reverse, that takes as input a list,
# and returns a new list that is the deep reverse of the input list.
# This means it reverses all the elements in the list, and if any
# of those elements are lists themselves, reverses all the elements
# in the inner list, all the way down.
# Note: The procedure must not change the input list.
# The procedure is_list below is from Homework 6. It returns True if
# p is a list and False if it is not.
def is_list(p):
return isinstance(p, list)
#For example,
def deep_reverse(n):
n.reverse()
for entry in n:
if is_list(entry):
entry.reverse()
deep_reverseA(entry)
return n
def deep_reverseA(n):
for entry in n:
if is_list(entry):
entry.reverse()
deep_reverseA(entry)
return n
p = [1, [2, 3, [4, [5, 6]]]]
print deep_reverse(p)
#>>> [[[[6, 5], 4], 3, 2], 1]
print p
#>>> [1, [2, 3, [4, [5, 6]]]]
q = [1, [2,3], 4, [5,6]]
print deep_reverse(q)
#>>> [ [6,5], 4, [3, 2], 1]
print q
#>>> [1, [2,3], 4, [5,6]]
My problem is that once I run the code the values of p and q change. How can I make them not change. I know that in python indexes are connected so if indexA = indexB and you change indexA then indexB will change. That is the problem I am having with fixing this problem.
I'll just tell you the answer right here, right now, with an explanation.
In python, variables are merely pointers to stored objects. So as you said in your post, if you declare foo = bar then foo not only is equal to bar, but foo is bar. This won't change unless you explicitly say so (for example you set bar = 2). So you need a way to make a copy of the original list.
There's this thing in python called list slicing, and I'm sure you heard of it. Basically you can get a portion of the list from indexA to indexB with my_list[indexA:indexB].
But you can also leave these spaces blank. indexA if not specified defaults to 0, and indexB defaults to -1 (the last element of the list).
So my_list[2:] returns all the elements from my_list[2] to my_list[-1]. Likewise, my_list[:3] returns my_list[0] to my_list[3].
Therefore, calling my_list[:] returns an exact copy of my_list, but not the actual list itself. This is what you need to do.
So applying it to your code:
def deep_reverse(n):
ncopy = n[:] #this is the part you need
#rest of function, replace `n` with `ncopy`
return ncopy
Also, do not apply this to deep_reverseA because in that function you are changing the original list in the copied list. You aren't changing the list you input into deep_reverse. If you did apply this to deep_reverseA, the lists wouldn't actually change (you would be returning a reverse of the copy but not the original)

Categories