Multiply every element in a nested list by a constant

Viewed 1296

I have been reading a lot of similar posts here, here, here, and so on, yet I cannot solve my problem. I have a list of list of pairs like this:

 my_list = [[[0, 3], [0, 0]], [[77, 94], [76, 94], [77, 93], [76, 93], [76, 90], [77, 84], [76, 88]], [[25, 31], [10, 0]]]

And I want to multiply each integer by -1. I have tried different things and get different errors, but this is the most logical code I have written in my opinion:

for p in range(len(my_list):
    for q in range(len(mylist[p])):
        my_new_list = [[i,j] * -1 for [i,j] in my_list[p][q]]

And this one is not even working! What I eventually want is something like this:

my_new_list = [[[0, -3], [0, 0]], [[-77, -94], [-76, -94], [-77, -93], [-76, -93], [-76, -90], [-77, -84], [-76, -88]], [[-25, -31], [-10, 0]]]

Could anyone help me with this please?

3 Answers

Your issue is that your innermost loop is creating a new sub-list, but not assigning it back (either to a new list, or my_list) appropriately. The previous iteration's result is re-written each time.

What you need is a list comprehension with three loops, one for each level of nesting:

my_new_list = [[[z * -1 for z in y] for y in x] for x in my_list]

If you can guarantee your innermost lists are always pairs, you can simplify this as per @ShadowRanger's suggestion and use a nested list comp with two loops,

my_new_list = [[[-a, -b] for a, b in x] for x in my_list]

This is equivalent to doing

import copy 

my_new_list = copy.deepcopy(my_list)
for x in my_new_list:
    for y in x:
        for i, z in enumerate(y): 
            y[i] = z * -1   # notice the assignment back to the sublist

To do it for a list of arbitrary depth, you could use this recursive function:

def negate(list_or_int):
    if isinstance(list_or_int, list):
        # It's a list, so call negate on every element
        return [negate(i) for i in list_or_int]

    # It's an int, so just return the negative
    return -list_or_int

my_new_list = negate(my_list)

A more efficient and elegant solution can be achieved through a recursive generator:

def kprod(k,itr):
    for x in itr:
        yield k*x if isinstance(x, (int, float)) else tuple(kprod(k,x))

Replace tuple with list if you need, but remind that tuple is slightly faster.

The function works with nested iterables of any deepness and type and returns a generator, so when you want to show its content you have to expand it by applying tuple or list to it:

>>> my_list = [[[0, 3], [0, 0]], [[77, 94], [76, 94], [77, 93], [76, 93], [76, 90], [77, 84], [76, 88]], [[25, 31], [10, 0]]]
>>> tuple(kprod(-1,my_list))
(((0, -3), (0, 0)), ((-77, -94), (-76, -94), (-77, -93), (-76, -93), (-76, -90), (-77, -84), (-76, -88)), ((-25, -31), (-10, 0)))

>>> tuple(kprod(2,[3]))
(6,)

>>> tuple(kprod(2,[4,(5,(-1,0)),6]))
(8, (10, (-2, 0)), 12)
Related