Python : Deep Reverse List - python

I have a nested list, and I need to reverse every element in the list.
Below is the example :
L = [[0, 1, 2], [1, 2, 3]]
Expected Output :
L = [[3, 2, 1], [2, 1, 0]]
I tried with the below piece of code, it works individually but when I am putting this code within function, then the list is not getting updated.
L = [list(reversed(row)) for row in L]
L.reverse()
This code works, and the List "L" is getting updated.
But when I put this code in function
def deep_rev(L):
L = [list(reversed(row)) for row in L]
L.reverse()
L = [[0, 1, 2], [1, 2, 3]]
deep_rev(L)
print(L)
This is returning the same list = [[0,1,2],[1,2,3]]
Can anyone please help me in this regard, why in function this is not working?

With L = ... you are assigning a new value to the parameter L within the function, without modifying the original list. Instead, you can use L[:] = ... to replace all the elements in the original list with the values from the new list.
def deep_rev(L):
L[:] = [list(reversed(row)) for row in reversed(L)]
L = [[0, 1, 2], [1, 2, 3]]
deep_rev(L)
print(L) # [[3, 2, 1], [2, 1, 0]]

Your current code creates a new list, rather than modifying the exiting list in place. You can make it work, just get rid of the list comprehension and do in-place reversals for the inner lists too:
def deep_rev(L):
for inner in L:
inner.reverse()
L.reverse()
If you want to support more than two levels of nesting, you could recursively call deep_rev on the inner lists, rather than just reversing them as I did above. You'd need to check if the values were lists or not, so you'd have a base case.

This is the solution I find out using recursion:
l=[[0, 1, 2], [1, 2, 3]]
def treereverse(l):
l.reverse()
for x in l:
if isinstance(x,list):
treereverse(x)
return l
print(treereverse(l))

This Code works for your problem,
L = [[0, 1, 2], [1, 2, 3]]
b=list(reversed(L))
q=[]
for i in b:
q.append(list(reversed(i)))
print q
Output:-
[[3, 2, 1], [2, 1, 0]]

def deep_rev(l):
l = [list(reversed(row)) for row in l]
l.reverse()
return l
l = [[0, 1, 2], [1, 2, 3]]
l = deep_rev(l)
print(l)
output
[[3, 2, 1], [2, 1, 0]]

deep_rev(L) modifies one local list, its scope is inside this function. Either make one deep copy of the list or return the reversed list.

Related

how to expand list in list that included in some not-iterable objects to flat? [duplicate]

This question already has answers here:
How to flatten a hetrogenous list of list into a single list in python? [duplicate]
(11 answers)
Closed 1 year ago.
I want to expand list in list that included in some not-iterable
objects to flat.
I tried to do this using list comprehension, but I get an error in not-iterable objects.
How to expand this list to flat?
# [[1, 2], 3] -> [1, 2, 3]
list = [[1, 2], 3]
flat = [item for sublist in list for item in sublist] # TypeError: 'int' object is not iterable
print(flat)
In my environment, numpy is installed in addition to the standard functions.
I tried numpy.concatenate(list).flat, but I get an error.
# [[1, 2], 3] -> [1, 2, 3]
list = [[1, 2], 3]
flat = numpy.concatenate(list).flat # ValueError: all the input arrays must have same number of dimensions, but the array at index 0 has 1 dimension(s) and the array at index 1 has 0 dimension(s)
print(flat)
If the iterables are only lists and only one level deep, you can do it in the list comprehension.
L = [[1, 2], 3]
flat = [v for item in L for v in (item if isinstance(item,list) else [item])]
If there are multiple levels and a variety of iterable types, you will probably need a recursive function:
def flatten(L):
if not isinstance(L,(list,tuple,set)): # you probably don't want str here
yield L
return
for F in L:
yield from flatten(F)
L = [[1, 2], 3, ({5,6,7},[8,9],10)]
flat = list(flatten(L))
print(flat)
[1, 2, 3, 5, 6, 7, 8, 9, 10]
You could try this to see if that what's you're looking for:
It can flatten any levels (deeply nested) by recursively calling itself. Just be aware this did not do performance test yet, so there may be room to improve it.
import collections
def flatten(L):
if isinstance(L, collections.Iterable):
return [a for i in L for a in flatten(i)] # recursively calling
else:
return [L]
Running it:
lst = [[1, 2], 3, [[4, 5], 6] ]
print(flatten(lst)) # [1, 2, 3, 4, 5, 6]
lst2 = [[1, 2], 3, [[4, 5, [6]]], 7, 8]
print(flatten(lst2)) # [1, 2, 3, 4, 5, 6, 7, 8] # deeply nested
There may be more elegant solutions, but the trivial one would be to just iterate with a couple of for loops, checking value type:
flat = []
for item in list:
try:
for subitem in item:
flat.append(subitem)
except TypeError: # Not iterable, append as is
flat.append(item)
Note this assumes the nesting is only one level deep.
First of all, I highly recommend avoid identifiers such as list, dict, set etc. as these are data-types from Python and even though the syntax is allowed, you will "hide" them from the usage in your application.
Also, as a suggestion, avoid using list comprehension for more complex operations, as they can become difficult to read.
I recommend the following approach:
my_list = [[1, 2], 3]
flat = []
for item in my_list:
if isinstance(item, list):
for val in item:
flat.append(val)
else: flat.append(item)
print(flat)
Using list comprehension, the solution would look like:
my_list = [[1, 2], 3]
flat = [v for item in my_list for v in (item if isinstance(item,list) else [item])]
print(flat)

How to append elements in a list within a list?

I'd like to append elements to a sub-list within a main-list. The number of elements to be appended corresponds to the sub-list position in the main-list.
My code:
# define the maximum level of the game
max_level = 3
# create list with length = level
lsx = []
# append empty lists in lsy for every element in lsx
empty = []
lsy = []
def create_list(max_level):
for i in range(max_level):
integer = random.randint(1,5)
lsx.append(integer)
for j in lsx:
lsy.append(empty)
return(lsy)
create_list(max_level)
Output:
[[], [], []]
Desired output:
[[1], [1, 2], [1, 2, 3]]
You don't need (and shouldn't) use globals. When appending empty to lsy multiple times you are appending the same object - changing one will change other as well.
You can create the nested lists inside the loop:
import random
def create_list(max_level):
# top level list
res = []
for i in range(max_level):
# nested list
next_l = []
for j in range(i+1):
next_l.append(random.randint(1, 5))
res.append(next_l)
return res
print(create_list(3))
Output:
[[5], [4, 3], [2, 3, 2]]
Well, this could work
def create_list(max_level):
res = []
for i in range(max_level):
i += 1
res.append(list(range(i+1))[1:])
return res
Output
>>> create_list(5)
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5]]

Separating sublist into multiple list and assigning dynamic list_name

I am trying to split a nested list into multiple lists and assign their name dynamically. Untill now, I tried the code below, but it only works when we have equal length sublists and we give them names manually.
sub_list = [[1,2,3],[4,5,5], [2,63,6]]
l1, l2, l3 = map(list, zip(*sub_list))
print(l1)
print(l2)
print(l3)
# Output
[1, 4, 2]
[2, 5, 63]
[3, 5, 6]
The approach above will fail when we have unequal length sublists such as (sub_list = [[1,2,3],[4,5], [2]]) and it does not give lists dynamic names.
I know it can be done by for loop, but I am not able to make list_name using a loop.
Any help will help me to reach more closure to my work
you could use zip_longest from itertools as follows:
sub_list = [[1,2,3],[4,5], [2]]
from itertools import zip_longest
l1, l2, l3 = map(list, zip_longest(*sub_list))
print(l1)
print(l2)
print(l3)
Output:
# [1, 4, 2]
# [2, 5, None]
# [3, None, None]
Answering the first question: If you don't want to give a manual name assing the map() to just one variable:
sub_list = [[1,2,3],[4,5,5], [2,63,6]]
rotated = map(list, zip(*sub_list))
for r in rotated:
print(r)
# Output
# [1, 4, 2]
# [2, 5, 63]
# [3, 5, 6]
Not completely sure what you want to accomplish, but I suggest you take a look at:
How to use itertools.zip_longest(): Python: zip-like function that pads to longest length? (You can filter out the Nones afterwards)
How to create dynamically named vars (although this is generally not the best thing to do): How do I create a variable number of variables?
The following code performs in both of your special cases:
There are no errors if some input lists are shorter than others
Names are procedurally/dynamically generated
def rotate_list_matrix(rows):
nrows = len(rows)
col_counts = map(lambda lyst: len(lyst), rows)
ncols = max(col_counts)
for ci in range(0, ncols): # column index
lyst = list()
list_name = "l" + str(ci + 1)
globals()[list_name] = lyst
for ri in range(0, nrows):
try:
lyst.append(rows[ri][ci])
except:
break
return
list_mata = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
list_matb = [[1, 2, 3],
[4, 5 ],
[7 ]]
rotate_list_matrix(list_matb)
print(l1)
print(l2)
print(l3)

unite lists if at least one value matches in python

Let's say I have a list of lists, for example:
[[0, 2], [0, 1], [2, 3], [4, 5, 7, 8], [6, 4]]
and if at least one of the values on a list is the same that another one of a different list, i would like to unite the lists so in the example the final result would be:
[[0, 1, 2, 3], [4, 5, 6, 7, 8]]
I really don't care about the order of the values inside the list [0, 1, 2, 3] or [0, 2, 1, 3].
I tried to do it but it doesn't work. So have you got any ideas? Thanks.
Edit(sorry for not posting the code that i tried before):
What i tried to do was the following:
for p in llista:
for q in p:
for k in llista:
if p==k:
llista.remove(k)
else:
for h in k:
if p!=k:
if q==h:
k.remove(h)
for t in k:
if t not in p:
p.append(t)
llista_final = [x for x in llista if x != []]
Where llista is the list of lists.
I have to admit this is a tricky problem. I'm really curious what does this problem represent and/or where did you find it out...
I initially have thought this is just a graph connected components problem, but I wanted to take a shortcut from creating an explicit representation of the graph, running bfs, etc...
The idea of the solution is this: for every sublist, check if it has some common element with any other sublist, and replace that with their union.
Not very pythonic, but here it is:
def merge(l):
l = list(map(tuple, l))
for i, h in enumerate(l):
sh = set(h)
for j, k in enumerate(l):
if i == j: continue
sk = set(k)
if sh & sk: # h and k have some element in common
l[j] = tuple(sh | sk)
return list(map(list, set(l)))
Here is a function that does what you want. I tried to use self-documenting variable names and comments to help you understand how this code works. As far as I can tell, the code is pythonic. I used sets to speed up and simplify some of the operations. The downside of that is that the items in your input list-of-lists must be hashable, but your example uses integers which works perfectly well.
def cliquesfromlistoflists(inputlistoflists):
"""Given a list of lists, return a new list of lists that unites
the old lists that have at least one element in common.
"""
listofdisjointsets = []
for inputlist in inputlistoflists:
# Update the list of disjoint sets using the current sublist
inputset = set(inputlist)
unionofsetsoverlappinginputset = inputset.copy()
listofdisjointsetsnotoverlappinginputset = []
for aset in listofdisjointsets:
# Unite set if overlaps the new input set, else just store it
if aset.isdisjoint(inputset):
listofdisjointsetsnotoverlappinginputset.append(aset)
else:
unionofsetsoverlappinginputset.update(aset)
listofdisjointsets = (listofdisjointsetsnotoverlappinginputset
+ [unionofsetsoverlappinginputset])
# Return the information in a list-of-lists format
return [list(aset) for aset in listofdisjointsets]
print(cliquesfromlistoflists([[0, 2], [0, 1], [2, 3], [4, 5, 7, 8], [6, 4]]))
# printout is [[0, 1, 2, 3], [4, 5, 6, 7, 8]]
This solution modifies the generic breadth-first search to gradually diminish the initial deque and update a result list with either a combination should a match be found or a list addition if no grouping is discovered:
from collections import deque
d = deque([[0,2] , [0,1] , [2,3] , [4,5,7,8] , [6,4]])
result = [d.popleft()]
while d:
v = d.popleft()
result = [list(set(i+v)) if any(c in i for c in v) else i for i in result] if any(any(c in i for c in v) for i in result) else result + [v]
Output:
[[0, 1, 2, 3], [8, 4, 5, 6, 7]]

Python: list of lists: replacing inner-lists with multiple/modified inner-lists

Consider the general case of the following list:
l = [['path/to/file', 0], ['path/to/folder$IDX/', 2], ['and/another/', 5]]
Meaning, a list of lists, where each list has a string on its first element that may or may not contain a special marker (in the example above it's $IDX) and some random integer on its second element.
My goal is to have a new list of lists, where every inner-list that has the special marker in its first element, will be replaced with X new lists, having X = 0, 1, 2..., n (where n is known) in place of the special marker.
For example, if n = 2, for the input above, the output should be:
l = [['path/to/file', 0], ['path/to/folder0/', 2], ['path/to/folder1/', 2], ['path/to/folder2/', 2], ['and/another/', 5]]
another example (again, for n = 2):
input:
l = [['random_text$IDX', 512], ['string', 2], ['more_$IDX_random_text', 5]]
output:
l = [['random_text0', 512], ['random_text1', 512], ['random_text2', 512], ['string', 2], ['more_0_random_text', 5], ['more_1_random_text', 5], ['more_2_random_text', 5]]
My first thought was dividing the original list to two list of lists, one that has the marker (l_with) and a second that hasn't (l_without), then processing l_with and adding it to l_without, but this rather naive attempt failed right away at the start:
>>> l = [['path/to/file', 0], ['path/to/folder$IDX/', 2], ['and/another/', 5]]
>>> l_with = [e for e in l if '$IDX' in e[0]]
>>> l_without = [e for e in l if '$IDX' not in e[0]]
>>> l_new = l_without + [[e[0].replace('$IDX', str(i)), e[1]] for i in range(3) for e in l_with]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: expected a character buffer object
In addition, even if the above would work, it's not that scalable... (consider a more general case: what if each inner-list has more than two elements? The only assumption I can make for now is that my (special-or-not) string will be the first element on each inner-list.)
What's a concise way of doing that? (scalable or not...)
This might help.
res = []
l = [['path/to/file', 0], ['path/to/folder$IDX/', 2], ['and/another/', 5]]
n = 2
for i in l:
if "$IDX" in i[0]:
for j in range(0, n+1):
res.append([i[0].replace("$IDX", str(j)) ] + i[1:])
else:
res.append(i)
print res
Output:
[['path/to/file', 0], ['path/to/folder0/', 2], ['path/to/folder1/', 2], ['path/to/folder2/', 2], ['and/another/', 5]]

Categories

Resources