This is my question for class:
• deepReverse(L) takes as input a list of elements where some of those elements may be lists
themselves. deepReverse returns the reversal of the list where, additionally, any element that is
a list is also deepReversed. Here are some examples:
deepReverse([1, 2, 3])
[3, 2, 1]
deepReverse([1, [2, 3], 4])
[4, [3, 2], 1]
deepReverse([1, [2, [3, 4], [5, [6, 7], 8]]])
[[[8, [7, 6], 5], [4, 3], 2], 1]
For this problem, you will need the ability to test whether or not an element in the list is a list itself. To this
end, you can use the following line of code to test whether or not x is a list:
if isinstance(x, list):
if True you will end up here
else:
if False you will end up here
this is the code i have so far:
def deepReverse(L):
if L == []:
return []
elif isinstance(L[0], list):
if True:
list2 = L[0]
return deepReverse(list2[1:]) + [list2[0]]
else:
deepReverse(L[1:]) + deepReverse(L[0])
else:
return deepReverse(L[1:]) + [L[0]]
for some reason it keeps returning [4,3,2,1] when i use the last test case. I do not understand why when I run the debugger once it gets to [3,4] going through each first element it just erases the second element. We are also not allowed to use any built in functions and must use recursion. Please help!
Assuming you cannot rely upon reversed -
def deep_rev(t):
if isinstance(t, list):
if not t:
return []
else:
return [ *deep_rev(t[1:]), deep_rev(t[0]) ]
else:
return t
print(deep_rev([1, [2, [3, 4], [5, [6, 7], 8]]]))
# [[[8, [7, 6], 5], [4, 3], 2], 1]
You can trade the *-unpacking for list concatenation, +, if you wish -
def deep_rev(t):
if isinstance(t, list):
if not t:
return []
else:
return deep_rev(t[1:]) + [ deep_rev(t[0]) ]
else:
return t
print(deep_rev([1, [2, [3, 4], [5, [6, 7], 8]]]))
# [[[8, [7, 6], 5], [4, 3], 2], 1]
The two programs above rely on list slicing, t[1:], to recur using a smaller list. Slicing the list for each iteration creates many intermediate values and we can avoid those using a different technique -
def deep_rev(t, k = 0):
try:
if isinstance(t, list):
v = t[k]
return deep_rev(t, k + 1) + [ deep_rev(v) ]
else:
return t
except IndexError:
return []
print(deep_rev([1, [2, [3, 4], [5, [6, 7], 8]]]))
# [[[8, [7, 6], 5], [4, 3], 2], 1]
Related
I've been trying sort the path of a graph.
For example, I have the following list in python.
graph = [
[4, 6], [6, 8], [8, 3], [3, 7], [7, 5], [5, 2], [1, 0], [0, 2], [4, 1]
]
The result needs to be,
graph = [
[0, 2], [2, 5], [5, 7], [7, 3], [3, 8], [8, 6], [6, 4], [4, 1], [1, 0]
]
0 -> 2 -> 5 -> 7 -> 3 -> 8 -> 6 -> 4 -> 1 -> 0
The premise is that the path begins with an edge whose initial value is zero (0) and ends with an edge whose last element is also zero.
Here is another example:
graph = [
[0, 4], [4, 6], [8, 3], [3, 7], [5, 2], [2, 1], [1, 0], [7, 6], [5, 8]
]
The result needs to be:
graph = [
[0, 1], [1, 2], [2, 5], [5, 8], [8, 3], [3, 7], [7, 6], [6, 4], [4, 0]
]
0 -> 1 -> 2 -> 5 -> 8 -> 3 -> 7 -> 6 -> 4 -> 0
The direction doesn't matter.
I started with this code.
def sort_graph(graph):
sorted_graph = []
for edge in graph:
if edge[0] == 0:
sorted_graph.append(edge)
for edge in graph:
if edge[0] != 0:
sorted_graph.append(edge)
return sorted_graph
but I'm not sure where to go from here.
For each node in the cycle, keep track of its two neighbors. You can then walk through these neighbors to produce an ordering of the nodes. Once you've reached a node where both neighbors have already been visited, you're done.
neighbors = {}
for fst, snd in graph:
neighbors.setdefault(fst, []).append(snd)
neighbors.setdefault(snd, []).append(fst)
seen_both_neighbors = False
current = 0
path = []
seen = set()
while not seen_both_neighbors:
path.append(current)
fst, snd = neighbors[current]
if fst not in seen:
seen.add(current)
current = fst
elif snd not in seen:
seen.add(current)
current = snd
else:
seen_both_neighbors = True
result = list(map(list, zip(path, path[1:] + [path[0]])))
print(result)
For both of your examples, this produces the correct answer down to ordering.
That was a fun problem to solve! Here's my idea for the approach:
Find all pairs (forward and backward)
Create a lookup table to easily navigate them
Start at 0, iterate through and remove nodes you've already visited
from itertools import chain
import random
graph = [
[4, 6], [6, 8], [8, 3], [3, 7], [7, 5], [5, 2], [1, 0], [0, 2], [4, 1]
]
# find all pairs independent of their direction
all_pairs = [*graph, *([t, f] for f, t in graph)]
# find all nodes
nodes = set(chain(*all_pairs))
# create a lookup dictionary for each point to show where you could go to
lookup = {node: {to_ for (from_, to_) in all_pairs if from_ == node} for node in nodes}
# simple solution - take a random path
from_ = 0
to_ = None
sorted_graph = []
while to_ != 0:
# select a random next point
to_ = random.choice(list(lookup[from_]))
# make sure to delete it so it doesn't get used again
lookup[from_].remove(to_)
lookup[to_].remove(from_)
# add to output
sorted_graph.append((from_, to_))
# tick one step forward
from_ = to_
print(sorted_graph)
You could also implement cycle,as below:
def cycle(vec):
result = [vec[0]]
s = vec[1:]
index = 0
while s:
if result[-1][0] == 0:
start = index
for i,v in enumerate(s):
if result[-1][1] in v:
del s[i]
result.append(v if v[0] == result[-1][1] else v[::-1])
break
index += 1
return result[start:] + result[:start]
cycle(graph)
[[0, 1], [1, 4], [4, 6], [6, 8], [8, 3], [3, 7], [7, 5], [5, 2], [2, 0]]
I have two lists like below:
A = [[0, [1, 2]], [1, [3, 4]], [2, [5, 6]], [3, [7, 8]], [4, [9, 10]], [5, [11, 12]], [6, [13, 14]]]
and
B = [[0, [1, 2]], [1, [4, 5]], [4, [[7, 8], [9, 10]]]]
I want to replace some elements of A based on some conditions related to list B.
I have written a code that does what I'm looking for, as below:
x = 3
v = [0, 1, 4]
for i in range (x):
if i in v and B[i][0] == A[i][0]:
A[i][1][0] = B[i][1][1]
for elem in v:
if elem not in range(x):
A[elem][1][0] = B[2][1][1][0]
A[elem+1][1][0] = B[2][1][1][1]
else:
A = A
print (A)
My problem is with these lines:
for elem in v:
if elem not in range (x):
A[elem][1][0] = B[2][1][1][0]
A[elem+1][1][0] = B[2][1][1][1]
As you can see, after looking through the elements of list v, and check if those elements are not in range (x), in this case, that element is 4, I want to replace some elements of A with some elements of B in this case that element is [4, [[7, 8], [9, 10]]] , However, the index of this element in list B is 2. Is there any other way to use 4 in [4, [[7, 8], [9, 10]]] which is also an element of v inside the code instead of writing B[2]? I want to use [x[0] for x in B] as the indicators instead of using the indexes, as those are different.
Thanks
if you want to stick to the structure of your current code, you could use np.where for this
x= 3
v = [0, 1, 4]
import numpy as np
for i in range (x):
if i in v and B[i][0] == A[i][0]:
A [i][1][0] = B[i][1][1]
for elem in v:
if elem not in range (x):
# get index (in this case: 2)
ind = np.where(np.array([i[0] for i in B]) == elem)[0][0]
A [elem][1][0] = B[ind][1][1][0]
A [elem+1][1][0] = B[ind][1][1][1]
else:
A = A
print (A)
This is quite different what I found in many threads - I don't mean to make list flat but unnest levels as follows:
[[[3, 3]]] should be [3, 3]
[[[3, 4], [3, 3]]] should be [[3, 4], [3, 3]] but not [3, 4], [3, 3] nor [3, 4, 3, 3] because this changes the structure completely.
Basically, I wanted to reduce levels to get the same len(a_list) in first and second iteration before loop break. But my idea is somewhat wrong:
This code works for anything but [[3], [4]]. Dunno what's wrong today because it worked yesterday. Need some help to correct this function. Now it returns [3] but should be unchanged.
# Unlevel list - reduce unnecessary nesting without changing nested lists structure
def unlevelList(l):
if len(l) > 0 and isinstance(l, list):
done = True
while done == True:
if isinstance(l[0], list):
if len(l) == len(l[0]):
l = l[0]
else:
l = l[0]
done = False
else:
done = False
return l
else:
return l
I'd be inclined to do this with recursion: if the object is a list of length 1, strip off the outer layer; then, recursively unlevel all of its children.
def unlevel(obj):
while isinstance(obj, list) and len(obj) == 1:
obj = obj[0]
if isinstance(obj, list):
return [unlevel(item) for item in obj]
else:
return obj
test_cases = [
[[[3, 3]]],
[[[3, 4], [3, 3]]],
[[3], [4]],
[[[3]]],
[[[3], [3, 3]]]
]
for x in test_cases:
print("When {} is unleveled, it becomes {}".format(x, unlevel(x)))
Result:
When [[[3, 3]]] is unleveled, it becomes [3, 3]
When [[[3, 4], [3, 3]]] is unleveled, it becomes [[3, 4], [3, 3]]
When [[3], [4]] is unleveled, it becomes [3, 4]
When [[[3]]] is unleveled, it becomes 3
When [[[3], [3, 3]]] is unleveled, it becomes [3, [3, 3]]
Edit: reading your question again, I think perhaps you want [[3], [4]] to remain [[3], [4]]. If that is the case, then I interpret the requirements to be "only strip off excess brackets from the top layer; leave inner one-element lists unaffected". In which case you don't need recursion. Just strip off the top list until you can't any more, then return it.
def unlevel(obj):
while isinstance(obj, list) and len(obj) == 1:
obj = obj[0]
return obj
test_cases = [
[[[3, 3]]],
[[[3, 4], [3, 3]]],
[[3], [4]],
[[[3]]],
[[[3], [3, 3]]]
]
for x in test_cases:
print("When {} is unleveled, it becomes {}".format(x, unlevel(x)))
Result:
When [[[3, 3]]] is unleveled, it becomes [3, 3]
When [[[3, 4], [3, 3]]] is unleveled, it becomes [[3, 4], [3, 3]]
When [[3], [4]] is unleveled, it becomes [[3], [4]]
When [[[3]]] is unleveled, it becomes 3
When [[[3], [3, 3]]] is unleveled, it becomes [[3], [3, 3]]
Id recommend a recursive solution as well
def unnest(l):
if isinstance(l, list) and len(l) == 1 and isinstance(l[0], list):
return unnest(l[0])
return l
Some test cases
test_cases = [
[[[3], [3, 3]]],
[[[3, 3]]],
[[[3, 4], [3, 3]]],
[[3], [4]],
[[[3]]]
]
for i in test_cases:
print(unnest(i))
gives
[[3], [3, 3]]
[3, 3]
[[3, 4], [3, 3]]
[[3], [4]]
[3]
This code seems to do exactly what you want. Maintain the lists as lists(but flat).
import itertools
a = [[[[1, 2]]], [[2, 3, 4, 5]], [[[[[[134, 56]]]]]], 9, 8, 0]
res = []
for element in a:
if isinstance(element, list):
while len(element) == 1:
element = list(itertools.chain(*element))
res.append(element)
else:
res.append(element)
print(res)
With the result res being [[1, 2], [2, 3, 4, 5], [134, 56], 9, 8, 0]
So I currently have a function to rotate a matrix counter-clockwise, the list is always a square grid:
def rotate(m: List[List[int]]) -> None:
temp = m.copy()
if len(m) > 1:
for x in range(len(m)):
for y in range(len(m)):
temp[len(m)-1-y][x] = m[x][y]
m = temp.copy()
I've traced this function repeatedly and to my knowledge it should be working. The problem is that for some reason every change to temp affects the original list. For example,
ORIGINAL =
[[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
WHAT SHOULD OUTPUT =
[[3, 6, 9],
[2, 5, 8],
[1, 4, 7]]
WHAT ACTUALLY OUTPUTS =
[[3, 6, 1],
[2, 5, 2],
[1, 2, 1]
I am on python ver 3.7.0 and have tried slipicing instead of copying the string too but the same thing happens. Anyone know why?
Since every item in the m list is a reference to a sublist, when you make a copy of the m list by calling m.copy(), it is only copying the references of the sublists without creating new copies of the sublists, which is why every change to the temp list is reflected on the original m list.
You should use copy.deepcopy() instead to make copies of the sublists as well:
from copy import deepcopy
def rotate(m):
temp = deepcopy(m)
if len(m) > 1:
for x in range(len(m)):
for y in range(len(m)):
temp[len(m)-1-y][x] = m[x][y]
return temp
so that:
rotate([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
returns:
[[3, 6, 9],
[2, 5, 8],
[1, 4, 7]]
Alternatively, you can implement the same matrix rotation by zipping the list of lists and reversing it:
def rotate(m):
return list(zip(*m))[::-1]
import copy
def rotate(m: [[int]]):
temp = copy.deepcopy(m)
if len(m) > 1:
for x in range(len(m)):
for y in range(len(m)):
temp[len(m)-1-y][x] = m[x][y]
return temp
So:
rotate([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
Output:
[[3, 6, 9],
[2, 5, 8],
[1, 4, 7]]
Example: https://onlinegdb.com/ryGU1jD6X
I've been trying to convert my list
alist = [[1,[1,2]],[2,[3,4,5]],[3,[1,2]],[4,[3,4,5]],[5,[5,6,7]],[6,[1,2]]]
into this. Since the second item of those two sublists are same.
[[[1,3,6],[1,2]],[[2,4],[3,4,5]]]
This is my code
alist = [[1,[1,2]],[2,[3,4,5]],[3,[1,2]],[4,[3,4,5]],[5,[5,6,7]],[6,[1,2]]]
lst=[]
for i in range(len(alist)):
inner = []
inner1=[]
for j in range(i+1,len(alist)):
if i+1 < len(alist):
if alist[i][1] == alist[j][1]:
inner1.append(alist[i][0])
inner1.append(alist[j][0])
inner.append(inner1)
inner.append(alist[i][1])
lst.append(inner)
print(lst)
but it gives this instead
[[[1, 3, 1, 6], [1, 2], [1, 3, 1, 6], [1, 2]], [[1, 3, 1, 6], [1, 2], [1, 3, 1, 6], [1, 2]], [[2, 4], [3, 4, 5]], [[3, 6], [1, 2]]]
It works when there's only 2 elements that are the same but when there's 3 it doesn't work.
Example
[2,4],[3,4,5] #2 of the same elements from alist works
[1,3,1,6],[1,2] #3 of the same elements from alist doesn't work
Can anyone please offer a solution?
You can use a dict (an Ordered one since you have to maintain the order) to group "heads" by "tails":
alist = [[1,[1,2]],[2,[3,4,5]],[3,[1,2]],[4,[3,4,5]],[5,[5,6,7]],[6,[1,2]]]
from collections import OrderedDict
c = OrderedDict()
for head, tail in alist:
c.setdefault(tuple(tail), []).append(head)
res = [[heads, list(tail)] for tail, heads in c.items()]
print res
prints
[[[1, 3, 6], [1, 2]], [[2, 4], [3, 4, 5]], [[5], [5, 6, 7]]]
If you want to omit 5 (a group with a single "head"), add a condition to the res= line:
res = [[heads, list(tail)] for tail, heads in c.items() if len(heads) > 1]