Python list comprehension with complex data structures - python

I'm trying to flatten some mixed arrays in Python using LC. I'm having some trouble figuring out how to structure it.
Here's the array's i'm trying to flatten
arr_1 = [1, [2, 3], 4, 5]
arr_2 = [1,[2,3],[[4,5]]]
I tried this methods for arr_1 but get "TypeError: 'int' object is not iterable"
print([item if type(items) is list else items for items in arr_1 for item in items])
So I decided to break it into parts to see where it's failing by using this
def check(item):
return item;
print([check(item) if type(items) is list else check(items) for items in [1, [2, 3], 4, 5] for items in arr_2])
Through the debugger I found that it's failing at the 2d array in
for items in [1, [2, 3], 4, 5]
I don't need the LC to be in one line but I just wanted to know how to do it in a single nested LC if its even possible.

Using an internal stack and iter's second form to simulate a while loop:
def flatten(obj):
return [x
for stack in [[obj]]
for x, in iter(lambda: stack and [stack.pop()], [])
if isinstance(x, int)
or stack.extend(reversed(x))]
print(flatten([1, [2, 3], 4, 5]))
print(flatten([1, [2, 3], [[4, 5]]]))
print(flatten([1, [2, [], 3], [[4, 5]]]))
Output (Try it online!):
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
To explain it a bit, here's roughly the same with ordinary code:
def flatten(obj):
result = []
stack = [obj]
while stack:
x = stack.pop()
if isinstance(x, int):
result.append(x)
else:
stack.extend(reversed(x))
return result
If the order doesn't matter, we can use a queue instead (inspired by 0x263A's comment), although it's less memory-efficient (Try it online!):
def flatten(obj):
return [x
for queue in [[obj]]
for x in queue
if isinstance(x, int) or queue.extend(x)]
We can fix the order if instead of putting each list's contents at the end of the queue, we insert them right after the list (which is less time-efficient) in the "priority" queue (Try it online!):
def flatten(obj):
return [x
for pqueue in [[obj]]
for i, x in enumerate(pqueue, 1)
if isinstance(x, int) or pqueue.__setitem__(slice(i, i), x)]

Related

Extend list with another list in specific index? [duplicate]

This question already has an answer here:
list extend() to index, inserting list elements not only to the end
(1 answer)
Closed last month.
In python we can add lists to each other with the extend() method but it adds the second list at the end of the first list.
lst1 = [1, 4, 5]
lst2 = [2, 3]
lst1.extend(lst2)
Output:
[1, 4, 5, 2, 3]
How would I add the second list to be apart of the 1st element? Such that the result is this;
[1, 2, 3, 4, 5 ]
I've tried using lst1.insert(1, *lst2) and got an error;
TypeError: insert expected 2 arguments, got 3
For those who don't like reading comments:
lst1 = [1, 4, 5]
lst2 = [2, 3]
lst1[1:1] = lst2
print(lst1)
Output:
[1, 2, 3, 4, 5]
If your only goal is to get the list sorted correctly, then you use .extend() and .sort() afterwards.
You can solve your problem in two steps:
Insert the list into the other list
Flatten the result
Code:
from collections.abc import Iterable
# https://stackoverflow.com/questions/2158395/flatten-an-irregular-arbitrarily-nested-list-of-lists
def flatten(xs):
for x in xs:
if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
yield from flatten(x)
else:
yield x
xs = [1,4,5]
ys = [2,3]
xs.insert(1, ys)
print("intermediate result", xs)
xs = flatten(xs)
print(xs)

how to combine same matching items in a 1d list and make it as 2d list [duplicate]

From this list:
N = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
I'm trying to create:
L = [[1],[2,2],[3,3,3],[4,4,4,4],[5,5,5,5,5]]
Any value which is found to be the same is grouped into it's own sublist.
Here is my attempt so far, I'm thinking I should use a while loop?
global n
n = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5] #Sorted list
l = [] #Empty list to append values to
def compare(val):
""" This function receives index values
from the n list (n[0] etc) """
global valin
valin = val
global count
count = 0
for i in xrange(len(n)):
if valin == n[count]: # If the input value i.e. n[x] == n[iteration]
temp = valin, n[count]
l.append(temp) #append the values to a new list
count +=1
else:
count +=1
for x in xrange (len(n)):
compare(n[x]) #pass the n[x] to compare function
Use itertools.groupby:
from itertools import groupby
N = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
print([list(j) for i, j in groupby(N)])
Output:
[[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5]]
Side note: Prevent from using global variable when you don't need to.
Someone mentions for N=[1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 1] it will get [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5], [1]]
In other words, when numbers of the list isn't in order or it is a mess list, it's not available.
So I have better answer to solve this problem.
from collections import Counter
N = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
C = Counter(N)
print [ [k,]*v for k,v in C.items()]
You can use itertools.groupby along with a list comprehension
>>> l = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
>>> [list(v) for k,v in itertools.groupby(l)]
[[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5]]
This can be assigned to the variable L as in
L = [list(v) for k,v in itertools.groupby(l)]
You're overcomplicating this.
What you want to do is: for each value, if it's the same as the last value, just append it to the list of last values; otherwise, create a new list. You can translate that English directly to Python:
new_list = []
for value in old_list:
if new_list and new_list[-1][0] == value:
new_list[-1].append(value)
else:
new_list.append([value])
There are even simpler ways to do this if you're willing to get a bit more abstract, e.g., by using the grouping functions in itertools. But this should be easy to understand.
If you really need to do this with a while loop, you can translate any for loop into a while loop like this:
for value in iterable:
do_stuff(value)
iterator = iter(iterable)
while True:
try:
value = next(iterator)
except StopIteration:
break
do_stuff(value)
Or, if you know the iterable is a sequence, you can use a slightly simpler while loop:
index = 0
while index < len(sequence):
value = sequence[index]
do_stuff(value)
index += 1
But both of these make your code less readable, less Pythonic, more complicated, less efficient, easier to get wrong, etc.
You can do that using numpy too:
import numpy as np
N = np.array([1,2,2,3,3,3,4,4,4,4,5,5,5,5,5])
counter = np.arange(1, np.alen(N))
L = np.split(N, counter[N[1:]!=N[:-1]])
The advantage of this method is when you have another list which is related to N and you want to split it in the same way.
Another slightly different solution that doesn't rely on itertools:
#!/usr/bin/env python
def group(items):
"""
groups a sorted list of integers into sublists based on the integer key
"""
if len(items) == 0:
return []
grouped_items = []
prev_item, rest_items = items[0], items[1:]
subgroup = [prev_item]
for item in rest_items:
if item != prev_item:
grouped_items.append(subgroup)
subgroup = []
subgroup.append(item)
prev_item = item
grouped_items.append(subgroup)
return grouped_items
print group([1,2,2,3,3,3,4,4,4,4,5,5,5,5,5])
# [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5]]

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]

Mirroring rows in matrix with loops/recursion?

Given some matrix, I need to mirror all the rows in the matrix. For example
[[2, 1],
[4, 3]]
would become
[[1, 2],
[3, 4]]
I managed to do it for the (2 x 2)-case. But I'm having trouble mirroring something like this:
[[1, 2, 3, 4],
[1, 2, 3, 4]]
This has to become
[[4, 3, 2, 1],
[4, 3, 2, 1]]
I want to do this with loops/recursion. If I use recursion, I would probably have as basic step that the inner most elements get swapped first, and then from here on we would make the matrix bigger by also including the outer elements and swapping them too. However, I'm having trouble with the recursion step. After having swapped the inner most elements, I want to include the next to inner most elements in the matrix, and swap them too, and then continue like this until we reach the outer elements. How can this be implemented in code? This is what I did so far:
matrix = [[1, 2, 3, 4],
[1, 2, 3, 4]]
def mirror(matrix):
# This corresponds to the basic step. The two inner most elements get swapped.
if len(matrix) == 2:
for i in range(len(matrix)):
for j in range(len(matrix)):
# Store one element in a temporal variable
temp = matrix[i][j]
matrix[i][j] = matrix[i][len(matrix) - 1]
matrix[i][len(matrix)-1] = temp
return matrix
else:
# Recursion step
for i in range(len(matrix)):
for j in range(len(matrix)):
return (matrix + mirror(matrix[(len(matrix) // 2) - 1 : len(matrix)]))
The recursion step is wrong I think. I tried using the slice operator, but I'm not sure how this should be done correctly. Any help with this problem would be appreciated.
A recursive solution is pretty trivial, just recurse across the array reversing each subarray:
arr= [[2, 1],
[4, 3]]
def reve(l):
# if we have recursed across all sub arrays just return empty list
if not l:
return []
# else reverse the first current sublist l[0] and recurse on the remaining sublists
return [l[0][::-1]] + reve(l[1:])
print(reve(arr))
[[1, 2], [3, 4]]
Which can be written concisely as:
def reve(l):
return [l[0][::-1]] + reve(l[1:]) if l else []
If you wanted it inplace:
arr = [[1, 2, 3, 4],
[1, 2, 3, 4]]
def reve(l):
if not l:
return
# call inplace list.reverse on each sublist
l[0].reverse()
return reve(l[1:])
reve(arr)
Output:
[[4, 3, 2, 1], [4, 3, 2, 1]]
And lastly we can achieve what you want inplace with no slicing at all using iter with the special method __length__hint:
def reve(l):
if l.__length_hint__() == 0:
return
sub = next(l)
sub.reverse()
return reve(l)
reve(iter(arr))
print(arr)
Output:
[[4, 3, 2, 1], [4, 3, 2, 1]]
Both functions might use the map function, but you can use a imperative for also. About my recursive approach, the else statement refers to all cases between the ultimate and second elements of the list, they are being concatenated until the first element is reached.
My recursive approach:
a = [[1, 2, 3],
[5, 6, 7]]
def mirror(matrix):
def revert(row):
if len(row) == 1:
return [row[0]]
else:
return [row[-1]] + revert(row[:-1]) # concatenates two lists.
return [revert(row) for row in matrix]
mirror(a)
My declarative approach:
def mirror(matrix):
def revert(row):
return row[::-1] # copies the array in reverse order
return list(map(revert, matrix)) #<-for python3, and just map(...) for python2
mirror(a)
Both functions outputs
[[3, 2, 1], [7, 6, 5]]
Actually, a more Pythonic way of doing that would be using list comprehensions. You can do that simply by:
matrix = [[1, 2, 3, 4],
[1, 2, 3, 4]]
reversed_matrix = (i[::-1] for i in matrix)
reversed_matrix will be a generator expression. You may convert it into a list by replacing "()" with "[]" In the list comprehension.
i[::-1] reverses the array in-place using slice operator

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]

Categories