Python deep reverse in a list - python

I have a nested list, and I need to reverse every element in the list. But I dont know whether the list is a list of list of list or not.
So example is:
p = [1, [2, 3, [4, [5, 6]]]]
print deep_reverse(p)
#>>> [[[[6, 5], 4], 3, 2], 1]
q = [1, [2,3], 4, [5,6]]
print deep_reverse(q)
#>>> [ [6,5], 4, [3, 2], 1]
What I have so far is:
def is_list(p):
return isinstance(p, list)
def deep_reverse(a):
a.reverse()
for i in a:
if is_list(i):
i.reverse()
print a
It works well for the second test, the q one, but doest work for the first test.
I am not sure do I need use a recursion to loop the whole thing? How can I modify my code? Thanks.

The reason your code doesn't work is because if i is a list of lists, you don't deep_reverse the lists within i.
You only need to change one line of your code to the following:
def is_list(p):
return isinstance(p, list)
def deep_reverse(a):
a.reverse()
for i in a:
if is_list(i):
deep_reverse(i) # <=== This is what changed
print a

def deep_reverse(lst):
try:
if len(lst) > 1:
return list(deep_reverse(item) for item in reversed(lst))
return lst
except TypeError:
return lst

def deep_reverse(L):
if L == []:
return L
elif type(L) == int:
return L
else:
return deep_reverse(L[1:]) + [deep_reverse(L[0])]
>>> print deep_reverse(p)
[[[[6, 5], 4], 3, 2], 1]
>>> print deep_reverse(q)
[[6, 5], 4, [3, 2], 1]
Hope this helps

>>> def deep_reverse(L):
for item in reversed(L):
if isinstance(item,list):
yield list(deep_reverse(item))
else:
yield item
>>> p = [1, [2, 3, [4, [5, 6]]]]
>>> q = [1, [2,3], 4, [5,6]]
>>> list(deep_reverse(p))
[[[[6, 5], 4], 3, 2], 1]
>>> list(deep_reverse(q))
[[6, 5], 4, [3, 2], 1]

Here is my solution
def is_list(p):
return isinstance(p, list)
def deep_reverse(p):
if p==[]:
return p
if not is_list(p[0]):
return deep_reverse(p[1:])+[p[0]]
else:
return deep_reverse(p[1:])+[deep_reverse(p[0])]

def deep_reverse(ls):
for i in ls:
if type(i)==list:deep_reverse(i)
ls.reverse()

here is a suggestion :
def deep_reverse(lst):
if isinstance(lst ,list):
if sum(1 for x in lst if isinstance(x, list)) == 0:
lst = lst[::-1]
return lst
else :
lst = lst[::-1]
lst = [deep_reverse(item) for item in lst]
return lst
else:
return lst

def is_list(p):
return isinstance(p, list)
def deep_reverse(p):
if p==[]:
return p
if not is_list(p):
return p
else:
return deep_reverse(p[1:])+[deep_reverse(p[0])]
test ok.

most elegant:
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()

I am going old school on this problem's butt:
def deepReverse(L):
"""Input a list tot output a reversed form of it"""
if not L:
return []
elif isinstance(L[-1], list):
return [deepReverse(L[-1])] + deepReverse(L[0:-1])
else:
return [L[-1]] + deepReverse(L[0:-1])

Related

Finding next max value from index of nested lists in python?

I'm trying to find the next maximum value of nested lists, I already have a nested list sorted by bubblesort, I need to take the largest element of each nested list and insert it into the solution vector, until the solution vector is sorted.
P.S: I can't delete the element from the initial nested list, only find the next maximum value.
See the image at the bottom as an example:
Nested_list = [[1, 7, 9], [4, 5, 6], [2, 3, 8], [0]]
The way I devised deleted the largest vector from the original list, which was quite time consuming, I believe that just moving the index to the next largest value will consume less time:
def bubbleSort(array):
n = len(array)-1
for i in range(n):
for j in range(0, n-i):
if array[j] > array[j+1]:
array[j], array[j+1] = array[j+1], array[j]
else:
continue
return array
def ordena_lista(output):
for sublista in output:
bubbleSort(sublista)
def maior_valor_lista(output):
return list(el[-1] for el in output)
def nested_remove(L, x):
if x in L:
L.remove(x)
else:
for element in L:
if type(element) is list:
nested_remove(element, x)
b = list(random.sample(range(10), 10))
n= m.floor(m.sqrt(len(b)))
output=list([b[i:i + n] for i in range(0, len(b), n)])
ordena_lista(b)
while output:
valores_maximo = maior_valor_lista(output)
var = max(valores_maximo, key=int)
final = [var] + final
nested_remove(output, var)
output = list(filter(None, output))
The simplest solution would be the following,
from functools import reduce
from operator import add
def sort_nested_list(nested_list):
return sorted(reduce(add, nested_list))
but, without knowing the exact implementation details of python's sorted, I can't tell you if it takes advantage of your pre-sorting.
If we know the sublists are sorted, and we are allowed to copy the list, and we know how many elements there are in total, we can write the following,
import math
from copy import deepcopy
def get_max_and_pop(nested_list):
""" find the maximum element of a sublist of nested_list, remove it from the sublist, and return it """
print(f"get_max_and_pop says: {nested_list}")
return max(nested_list, key=lambda x: x[-1:]).pop()
def sort_nested_list_whose_sublists_are_sorted(nested_list, n_elements):
nested_list_copy = deepcopy(nested_list)
return [get_max_and_pop(nested_list=nested_list_copy) for _ in range(n_elements)][::-1]
edit: without knowledge of the number of elements, we can write,
from copy import deepcopy
def sort_nested_list_whose_sublists_are_sorted_iter(nested_list):
nested_list_copy = deepcopy(nested_list)
while any(nested_list_copy):
yield max(nested_list_copy, key=lambda x: x[-1:]).pop()
This amounts to a bizarre, woefully inefficient and completely unnecessary sorting algorithm but here goes anyway:
Nested_list = [[9, 7, 1], [4, 5, 6], [2, 3, 8], [0]]
for e in Nested_list:
e.sort()
Output_list = []
Nested_list_copy = [[e_ for e_ in e] for e in Nested_list]
element_count = sum(len(e) for e in Nested_list)
for _ in range(element_count):
m = None
for i, e in enumerate(Nested_list_copy):
if e:
tm = e[-1]
if m is None or tm > m:
m = tm
k = i
Output_list.insert(0, Nested_list_copy[k].pop())
print(Nested_list)
print(Output_list)
Output:
[[1, 7, 9], [4, 5, 6], [2, 3, 8], [0]]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

How do I heappush lists from a matrix by a certain index while preserving the list?

Say I have a list:
l1 = [[1, 3], [3, 2], [2, 1]]
I want to push each item in l1 to a binary heap, 'memory' but sorted in the binary heap by each_item[-1].
I've tried: heapq.heappush(_heap, item=itemgetter(-1)) and the equivilent using an anonymous function but I get:
TypeError: heappush() takes no keyword arguments
You can store entries in the heap as 3-element tuples including the last element, an entry count, and the actual item. This way the items will be sorted by their last values with the entry count ensuring sort stability (i.e. two items with equal last elements are returned in the order they were added):
>>> import heapq
>>> heap = []
>>> l1 = [[1, 3], [3, 2], [2, 1]]
>>> for count, item in enumerate(l1):
... heapq.heappush(heap, (item[-1], count, item))
...
>>> while heap:
... print(heapq.heappop(heap)[-1])
...
[2, 1]
[3, 2]
[1, 3]
One option is to make small wrappers around heapq functions to prepend/extract the sorting value to/from the item in a consistent way:
def heappush(h, item, key=lambda x: x):
heapq.heappush(h, (key(item), item))
def heappop(h):
return heapq.heappop(h)[1]
def heapify(h, key=lambda x: x):
for idx, item in enumerate(h):
h[idx] = (key(item), item)
heapq.heapify(h)
Testing with your sample:
l1 = [[1, 3], [3, 2], [2, 1]]
h = []
for item in l1:
heappush(h, item, key=itemgetter(-1))
while h:
print(heappop(h))
Prints:
[2, 1]
[3, 2]
[1, 3]
Note that you could use h=l1; heapify(h, key=itemgetter(-1)) which should be faster than individually heappushing each item.
I like Eugene's solution but if working with this special tuple a lot, one could define a custom object to achieve this as follows:
from heapq import heappush, heappop
class MyTupleHeapObj(object):
def __init__(self,val): self.val = val
def __lt__(self,other): return self.val[-1] < other.val[-1]
def __eq__(self,other): return self.val == other.val
def __str__(self): return str(self.val)
h = []
for item in [[1, 3], [3, 2], [2, 1]]:
heappush(h, MyTupleHeapObj(item))
while h:
print heappop(h)
Prints the following:
[2, 1]
[3, 2]
[1, 3]

Making a multidimensional list flat where the sublists are equal to their average

So for example, I have the list = [1, [2, [3, [5, [5]]]]] the resulting list would be [1,3].
I currently have this,
def avg(mylist):
if mylist == []:
return mylist
elif type(mylist[0]) == list:
mylist[0] = 0 # average of mylist[0]
return mylist[:1]+avg(mylist[1:])
elif type(mylist[0]) == float:
return mylist[:1]+avg(mylist[1:])
Which works the way I want it too but I cannot find a way to set mylist[0] = the average of mylist[0]. I have also tried it a lot of different ways but I cannot find one that works.
EDIT: Another example of something I tried.
total = 0
nosublist=True
if mylist == []:
return mylist
for x in mylist:
if type(x) == list:
nosublist=False
if nosublist:
return mylist[:1]+average(mylist[1:])
elif not nosublist:
for x in mylist:
if type(x) == list:
total += average(x)
else:
total += x
mylist[0] = total/len(mylist)
return average(mylist[:1])+average(mylist[1:])
def isiter(x):
try:
iter(x)
return True
except TypeError:
return False
def _flatten(x, reduce=iter):
for i in x:
if isiter(i):
r = reduce((j for j in _flatten(i, reduce=reduce)))
if isiter(r):
yield from r
else:
yield r
else:
yield i
Now you can plugin mean
def mean(x):
l = list(x)
return sum(l)/len(l)
l = [1, [2, [3, [5, [5]]]]]
list(_flatten(l, reduce=mean))
>>> [1, 3.0]
or
mean(flatten(l, reduce=mean))
>>> 2.0
EDIT:
If you really need only a single function:
def flatten(x, reduce=iter):
return reduce(_flatten(x, reduce=reduce))
This isn't probably the best solution but you could use it help make yours even better! I created two lists on each recursive call one with only elements that aren't lists and one with elements that were all lists (just in case you had a format like [1, 3, [2], [2], [3, [5, [5, 5]]]]) and created a call stack to take the sum of the elements in each inner array and one to take the length of each inner array and then took the average. Of course since there might be multiple arrays in the list of arrays you can map this same functionality for each array and accumulate their avg's.
code
list1 = [1, [2, [3, [5, [5]]]]]
list2 = [1, [2, 3], [4, 5], [5, [3, 4]]]
def avg(mylist):
"""flattens an array where the sublists to flatten are the average of that sublist"""
subarrays = filter(lambda x: type(x) == type([]), mylist)
rootelems = filter(lambda x: type(x) != type([]), mylist)
avg_all = lambda elem: sum((avg(elem))) / len(avg(elem))
if subarrays == []:
return mylist
return rootelems + map(avg_all, subarrays)
print avg(list1)
print avg(list2)
result
[1, 3]
[1, 2, 4, 4]

Using (trying to) recursion to reverse lists within a list

def is_list(p):
return isinstance(p, list)
def deep_reverse(p):
initial = []
for v, e in enumerate(p):
if is_list(e):
#print p[v][::-1]
initial.append(p[v][::-1])
deep_reverse(e)
return initial
p = [1, [2, 3, [4, [5, 6, [7, 8]]]]]
print deep_reverse(p)
I get [[[4, [5, 6, [7, 8]]], 3, 2]], expected at least (I haven't bothered to figure out how to not lose the very first list [1[...]] yet) [[[[6, 5, [8, 7]], 4], 3, 2]].
As you can see the code only reverses [ [2, 3]] --> [[3, 2]].
What did I do wrong? Haven't I though about?
This is how I would do it:
def deep_reverse(p):
return [deep_reverse(x) if isinstance(x, list) else x for x in p[::-1]]
p = [1, [2, 3, [4, [5, 6, [7, 8]]]]]
print deep_reverse(p) # [[[[[8, 7], 6, 5], 4], 3, 2], 1]
A more generic, Pythonic answer to this, based on Pavel Anossov's is as follows:
def deep_reversed(seq):
return [deep_reversed(x) if (isinstance(x, collections.Sequence) and
not isinstance(x, str)) else x
for x in reversed(seq)]
Note that this is for Python 3.x, in Python 2.x, you will want isinstance(x, basestring) instead to allow for Unicode strings.
This answer is a good one, as it will work correctly with any object that acts as a sequence - be it a list, a tuple, or a custom class. This means it's much more flexible.
Edit: If you wanted it to reverse strings internally:
def deep_reversed(seq):
for x in reversed(seq):
if isinstance(x, collections.Sequence):
if isinstance(x, str):
yield "".join(reversed(x))
else:
yield deep_reversed(x)
else:
yield x
Again, in 2.x, use isinstance(x, basestring).
There are already many nice solutions, but maybe this is the algorithm you are trying:
def is_list(p):
return isinstance(p, list)
def deep_reverse(p):
initial = p[::-1] # reverse this level
for v, e in enumerate(initial):
if is_list(e): # for all the sublist in this level
initial[v] = deep_reverse(e) # recursively call deep_reverse to reverse the sublist
return initial
p = [1, [2, 3, [4, [5, 6, [7, 8]]]]]
print deep_reverse(p)
In the recursive calls to deep_reverse(e) you are not using the returned value. It looks as though you are expecting it to modify the input list
You can change it to something like this:
def deep_reverse(p):
initial = []
for e in p[::-1]:
if is_list(e):
initial.append(deep_reverse(e)])
else:
initial.append(e)
return initial
This will solve your purpose:
import collections
def dr(p):
r=[]
for i in p:
if isinstance(i,collections.Iterable):
r.append(dr(i))
else:
r.append(i)
return r[::-1]

What's wrong with my Python code? I'm trying to deep-reverse a list input

def is_list(p):
return isinstance(p, list)
def deep_reverse(list):
o=[]
for i in reversed(list):
if is_list(i)==True:
print i
deep_reverse(i)
o.append(i)
return o
For example:
p = [1, [2, 3, [4, [5, 6]]]]
print deep_reverse(p)
#>>> [[[[6, 5], 4], 3, 2], 1]
Change the line
deep_reverse(i)
to
i = deep_reverse(i)
Incidentally, a shorter way to write this function would be:
def deep_reverse(lst):
if not is_list(lst):
return lst
return map(deep_reverse, reversed(lst))

Categories