Python Reversing Certain Parts of Tuple - python

Suppose i have the following tuple as:
a = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
What i want to do is reversing certain parts of the tuple.
For example 4 elements stays same and then 4 elements will be reversed.
I want to get the following result:
a = (1,2,3,4,8,7,6,5,9,10,11,12,16,15,14,13,17,18,19,20)
How can i achieve that (as pythonian kind of way)?
Thanks...

Tuples are immutable, however, by converting a to a list, in-place assignment can be performed:
a = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
new_a = list(a)
for c, i in enumerate(range(0, len(a), 4)):
if c%2:
new_a[i:i+4] = new_a[i:i+4][::-1]
print(tuple(new_a))
Output:
(1, 2, 3, 4, 8, 7, 6, 5, 9, 10, 11, 12, 16, 15, 14, 13, 17, 18, 19, 20)

The direct, simple, readable answer (pythonic?):
a = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
for n in range(0, len(a), 8):
a[n+4:n+8] = a[n+7:n+3:-1]
print(a)

Here's a generator-based solution. The benefits of doing it this way is that this solution doesn't require the input to be sliceable, so you can apply it to the output of functions like zip and map.
from itertools import zip_longest as zipl
from itertools import cycle, chain
_marker = object()
def cycle_map(iterable, func_iterable):
funcs = cycle(func_iterable)
for func, item in zip(funcs, iterable):
if func:
yield func(item)
else:
yield item
def reverse_filter(iterable, remove=_marker):
t = tuple(i for i in iterable if i is not remove)
return reversed(t)
def reverse_alternating(iterable, length=4):
chunks = zipl(*[iter(iterable)]*length, fillvalue=_marker)
funcs = (None, reverse_filter)
return chain.from_iterable(cycle_map(chunks, funcs))
a = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
list(reverse_alternating(a))
# [1, 2, 3, 4, 8, 7, 6, 5, 9, 10, 11, 12, 16, 15, 14, 13, 17, 18, 19, 20]

Using list slicing.
Ex:
from itertools import chain
a = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
res = [a[v:v+4] if i%2 == 0 else list(reversed(a[v:v+4])) for i, v in enumerate(range(0, len(a), 4))]
print(tuple(chain.from_iterable(res))) #Flatten list
Output:
(1, 2, 3, 4, 8, 7, 6, 5, 9, 10, 11, 12, 16, 15, 14, 13, 17, 18, 19, 20)

Just another way to approach the problem using list comprehension (not the fastest probably). Wanted to share the logic, assuming that len(a) is divisible by 4:
a = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
lst = [a[j:j+4] if i%2 == 0 else a[j:j+4][::-1] for i, j in
enumerate(range(0,len(a), 4))]
result = tuple([j for i in lst for j in i])
print (result)
Output
(1, 2, 3, 4, 8, 7, 6, 5, 9, 10, 11, 12, 16, 15, 14, 13, 17, 18, 19, 20)

Related

sum of elements in list, when an element can take two different values

I have a list of elements and want to get the sum of this list:
a = [4, 5, "X", "X"].
But X can be 2 different values, 2 or 3. So there are 4 sums of this list:
sum1 = sum([4, 5, 2, 2]) = 13
sum2 = sum([4, 5, 2, 3]) = 14
sum3 = sum([4, 5, 3, 2]) = 14
sum4 = sum([4, 5, 3, 3]) = 15
Basically I want to get a tuple (or list) of all possible sums of the list, like:
sums = (13, 14, 14, 15)
For an input list with 0 X, I want to get a tuple with 1 element, for a tuple with 1 X a tuple of 2 elements, with 2 X tuple with 4 elements...with n X a tuple of 2^n elements.
Similar to Michael's answer, also using itertools.product, but with a dictionary mapping elements to possible values. This might be more flexible, allowing placeholders in all positions and different values for different placeholders, and also makes the code a bit simpler IMHO.
>>> from itertools import product
>>> a = [4, 5, "X", "X"]
>>> vals = {"X": [2, 3]}
>>> [sum(p) for p in product(*[vals.get(x, [x]) for x in a])]
[13, 14, 14, 15]
You can use itertools.product and a list comprehension
from itertools import product
a = [4, 5, "X", "X"]
r = a.count('X')
[sum(a[:len(a)-r] + [*i]) for i in product([2,3], repeat=r)]
Output
[13, 14, 14, 15]
Testing with more cases
#test cases
for n in range(5):
a=[4,5, *['X']*n] # [4,5] .... [4,5,'X','X','X','X']
r = a.count('X')
print([sum(a[:len(a)-r] + [*i]) for i in product([2,3], repeat=r)])
Output
[9]
[11, 12]
[13, 14, 14, 15]
[15, 16, 16, 17, 16, 17, 17, 18]
[17, 18, 18, 19, 18, 19, 19, 20, 18, 19, 19, 20, 19, 20, 20, 21]

Skipping iterations in python at counting down intervals

I have a list (fullList) [5, 8, 9, 10, 5, 9, 10, 11, 8, 9, 13, 14, 9, 10, 13, 15, 10, 11, 14, 15] and a value n, - I would like to iterate the first (n) 4 values, then skip 1 number, then iterate the next 4-1 value, and skip 2 numbers, then iterate the next 4-2 numbers and skip 3 numbers, etc... until the list is fully iterated.
My final answer should look like this:
[5,8,9,10,9,10,11,13,14,15]
It can be obtained by taking the first 4 values (5,8,9,10), then skipping 1 count (5), then take the next 3 values (9,10,11) and skipping 2 counts (8,9), then taking the next 2 values (13,14) and skipping 3 counts (9,10,13), taking the next value (15) and skipping 4 counts (10,11,14,15)
--
Edit: I have obtained fullList from iterating the values of a smaller list (listb) [2,3,6,7,8] by adding it against itself. I have solved this part, but still would like to understand the first part (above).
I would like to achieve the following results:
2+3 = 5
2+6 = 8
2+7 = 9
2+8 = 10
3+6 = 9
3+7 = 10
3+8 = 11
6+7 = 13
6+8 = 14
7+8 = 15
Thank you!
You can use itertools.chain
from itertools import chain
x = [5, 8, 9, 10, 5, 9, 10, 11, 8, 9, 13, 14, 9, 10, 13, 15, 10, 11, 14, 15]
x = list(chain.from_iterable(x[4*idx + idx:4*idx + 4] for idx in range(4)))
For your second part, use itertools.combinations
from itertools import combinations
x = [2,3,6,7,8]
for combo in combinations(x, 2):
a, b = combo
print(f"{a} + {b} = {sum(combo)}")
Using a nested list comprehension:
from itertools import chain
full_list = [5, 8, 9, 10, 5, 9, 10, 11, 8, 9, 13, 14, 9, 10, 13, 15, 10, 11, 14, 15]
n = 4
list(chain.from_iterable(x[i:] for i,x in enumerate(
[x for x in [full_list[i*n:i*n+n] for i in range(n+1)]])))
# [5, 8, 9, 10, 9, 10, 11, 13, 14, 15]
The nested list comprehension:
[x for x in [full_list[i*n:i*n+n] for i in range(n+1)]]
groups the list into sublists of n elements. The outer list comprehension then extracts from those sublists, further sublists of the required length using enumerate() to determine the start offset.
Finally itertools.chain.from_iterable() is used to flatten the sublists into a single list as required.
Well, I made it in 2 different ways:
The first one iterates list element by element with 2 separate counters, the skip counter and the array number counter (counter), when the counter gets fill to the n number (counter == n), it gets into a for loop and starts poping out of the main list the elements (skipping the future iterations as you told us you want to do), when the counterSkip gets to 0, it resets all the counters, reduces the n variable by 1 and increments the number of numbers you want to skip by one (skipN) until the array is full iterated
counter = 0
n= 4
skipN = 1
counterSkip = skipN
array = [5, 8, 9, 10, 5, 9, 10, 11, 8, 9, 13, 14, 9, 10, 13, 15, 10, 11, 14, 15]
for index in enumerate(array):
if counter >= n and counterSkip != 0:
for i in range(counterSkip):
array.pop(index[0])
counterSkip -= 1
n -= 1
skipN += 1
counter = 0
counterSkip = skipN
counter += 1
print(array)
#
[5, 8, 9, 10, 9, 10, 11, 13, 14, 15]
And the second way (It's almost identical as one answer here):
Using the extend() method to add elements to a new list setting lower bound of the main list to a var named "index" that updates it's value summing itself to the n var +1 to preserve a continuity in the iteration, and the upper bound of the main list to the index itself minus i, that serves as a accumulator of how many elements you want to skip each iteration.
You can read more about the "extend" method right here: https://www.programiz.com/python-programming/methods/list/extend
newList = []
index = 0
i = 0
n= 4
list = [5, 8, 9, 10, 5, 9, 10, 11, 8, 9, 13, 14, 9, 10, 13, 15, 10, 11, 14, 15]
while(index<len(list)):
newList.extend(list[index:index+(n-i)])
index+=(n+1)
i+=1
print(newList)
#
[5, 8, 9, 10, 9, 10, 11, 13, 14, 15]
Edit: Ignore the second/alternative part of the question - I managed to get the list that I was looking for
lista = [2,3,6,7,8]
count = 0
originalCount = 0
fullList = []
while count < len(lista):
for j in range(len(lista)):
if (count != j):
if (j > count):
fullList.append(lista[count]+lista[j])
elif (count != originalCount):
j+=count
count+=1
OK, since this seems to have turned into a kind of golfing contest, here's mine:
def upper_left(arr, n):
"""Arguments:
arr is an iterator returning a flattened n x n array.
Since no element is selected from the last row, it can be
just for the first n-1 rows.
Returns:
An iterator which produces the flattened upper-left triangle
of arr, excluding the anti-diagonal.
"""
return (z for z, (i, j) in zip(arr, ((i,j)
for i in range(n)
for j in range(n)))
if i + j < n - 1)
print(list(upper_left(
[
5, 8, 9, 10, 5,
9, 10, 11, 8, 9,
13, 14, 9, 10, 13,
15, 10, 11, 14, 15
], 5)))
This zips the array against the corresponding row/column indices, and then selects the elements for which the indices are above and to the left of the anti-diagonal (that is, the diagonal from the lower-left corner to the upper-right corner).
Running the above file produces:
$ python upper_left.py
[5, 8, 9, 10, 9, 10, 11, 13, 14, 15]
It would arguably be easier to combine the selection of elements with the generation of the elements in the first place. But this function composes well. (It's probably worth writing the functions which produce the other triangles. The only difference is the comparison in the last line of the function.
Personally I think using itertools makes it more complex than it needs to be.
This solution should work.
new_list = []
index = 0
i = 0
while(index<len(fullList)):
new_list.extend(fullList[index:index+(n-i)])
index+=(n+1)
i+=1
Edit
fullList = [5, 8, 9, 10, 5, 9, 10, 11, 8, 9, 13, 14, 9, 10, 13, 15, 10, 11, 14, 15]
new_list = []
index = 0
i = 0
n = 4
while(index<len(fullList)):
new_list.extend(fullList[index:index+(n-i)])
index+=(n+1)
i+=1
print(new_list)
#[5, 8, 9, 10, 9, 10, 11, 13, 14, 15]

Check if element of a list is divisible by all elements of another list

I have two lists:
s = [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
a = [2,6]
I want to print all numbers of s list that are both divisible by a[0] and a[1].
In a case like this I would simply do:
for num in s:
if num % a[0] == 0 and num % a[1] == 0:
print(num)
But let's assume I don't know how long the list is. How can I get it right?
I've tried to figure this out for some time now, but I am stuck.
You can use the all function with a generator expression like this:
if all(num % i == 0 for i in a):
You can use a nested list-comp with all, eg:
s = [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
a = [2,6]
result = [x for x in s if all(x % y == 0 for y in a)]
Gives you:
[6, 12, 18, 24]
you could calculate the least common multiple of a first:
from math import gcd
a = [2, 6]
s = [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
lcm = a[0] * a[1] // gcd(a[0], a[1])
print(list(x for x in s if x % lcm == 0)
# [6, 12, 18, 24]
this may be more efficient if your a is longer than just 2 elements.
in order to get the LCM of a list of elements you could use lcm_lst as defined below:
from math import gcd
def lcm(a, b):
return a * b // gcd(a, b)
def lcm_lst(a):
l = a[0]
for x in a[1:]:
l = lcm(l, x)
return l

How to populate a 2d array?

I have the following code that (nearly) populates a list of lists (I will call it a 2d array) in Python.
Instead of going up from 0-6 and repeating this 3 times, I want it to populate the array with numbers 0 - 20. Please see code below:
#matrix=[1,2,3,4,5,6,7],[8,9,10,11,12,13,14],[15,16,17,18,19,20,21]
#print(matrix[1][2])
rows=3
columns=7
for i in range(rows):
for j in range(columns):
matrix=[j]
i=i+1
print(matrix,end="")
The erroneous output is:
[0][1][2][3][4][5][6][0][1][2][3][4][5][6][0][1][2][3][4][5][6]
I want the output to be:
[1,2,3,4,5,6,7],[8,9,10,11,12,13,14],[15,16,17,18,19,20,21]
There are fancier ways, but this is the most straightforward:
>>> rows = 3
>>> columns = 7
>>> n = 1
>>> matrix = []
>>> for _ in range(rows):
... sub = []
... for _ in range(columns):
... sub.append(n)
... n += 1
... matrix.append(sub)
...
>>> matrix
[[1, 2, 3, 4, 5, 6, 7], [8, 9, 10, 11, 12, 13, 14], [15, 16, 17, 18, 19, 20, 21]]
And for good measure, a fancy way:
>>> import itertools
>>> counter = itertools.count(1)
>>> rows = 3
>>> columns = 7
>>> matrix = [[n for n, _ in zip(counter, range(columns))] for _ in range(rows)]
>>> matrix
[[1, 2, 3, 4, 5, 6, 7], [9, 10, 11, 12, 13, 14, 15], [17, 18, 19, 20, 21, 22, 23]]
>>>
Use list comprehension. You want 3 rows so the base of the list comprehension is: [for y in range(rows)]. You want to have incrementing numbers starting with a number divisible by columns but starting from 1 so: range(columns*y+1,...) and you want to have columns range (7) so range(columns*y+1,columns+(columns*y+1)) and then turn that into a list.
rows=3
columns=7
matrix=[list(range(columns*y+1,columns+(columns*y+1))) for y in range(rows)]
print(matrix)
#outputs: [[1, 2, 3, 4, 5, 6, 7], [8, 9, 10, 11, 12, 13, 14], [15, 16, 17, 18, 19, 20, 21]]

Python - Function that sums list's with error

def somalist (lista):
listasoma = [0,0,0,0,0,0,0,0,0,0,0]
for i in lista:
for g in i:
if i.index(g) != 0 and i.index(g) != 1 and i.index(g) != 2 and i.index(g) != 3:
listasoma[i.index(g)] += g
else:
listasoma[i.index(g)] = g
print(listasoma)
return(listasoma)
x = [[1,2,3,4,5,6,7,8,9,10,11],[1,2,3,4,5,6,7,8,9,10,11],[1,2,3,4,5,6,7,8,9,10,11]]
print(somalist(x))
is that the function, it works doing the sum of each sublist but it preserves the the index [0], [1], [2] and [3] and sum the anothers. In this example I will use the list x, in this list the function works correctly. The exit of the program is:
[1, 2, 3, 4, 32, 18, 21, 24, 27, 30, 22]
is what I've said it preserves the members [0],[1],[2],[3] and sum the other indices of each sublist.
But when we use some number more than once, it fails.
using:X = [[1,2,3,4,5,6,7,8,9,10,11],[1,2,3,4,**6**,6,7,8,9,10,11],[1,2,3,4,5,6,7,8,9,10,11]]
the 6 (index 4) in second sublist was writen twice (the another at the index 5 at the same sublist)
the exit was :[1, 2, 3, 4, **22**, 12, 21, 24, 27, 30, 33]
It's wrong!
The correct exit should be: [ 1, 2, 3, 4, 16, 18, 21, 24, 27, 30, 33]
Can someone tell me where my code is wrong?
I would recommend ditching the index() approach entirely.
def somalist (lista):
return lista[0][:4] + [sum(item) for item in list(zip(*lista))[4:]]
This works on lists with repeated elements.
v
>>> x = [[1,2,3,4,5,6,7,8,9,10,11],[1,2,3,4,5,6,7,8,9,10,11],[1,2,3,4,5,6,7,8,9,10,11]]
>>> X = [[1,2,3,4,5,6,7,8,9,10,11],[1,2,3,4,6,6,7,8,9,10,11],[1,2,3,4,5,6,7,8,9,10,11]]
>>> print(somalist(x))
[1, 2, 3, 4, 15, 18, 21, 24, 27, 30, 33]
>>> print(somalist(X))
[1, 2, 3, 4, 16, 18, 21, 24, 27, 30, 33]
^^

Categories

Resources