Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alter all values in a Python list of lists?

Tags:

python

Let's say I have a list like:

my_list = [[1,2,3],[4,5,6],[7,8,9]]

How do I alter every value in the list without doing?:

for x in range(0, 3):
    for y in range(0, 3):
        my_list[x][y] = -my_list[x][y]

I have tried to simplify this by doing

my_list = [[[-a, -b, -c] for [a, b, c] in d] for d in my_list]

but the values remain the same.

like image 760
James Sumners Avatar asked Mar 28 '09 22:03

James Sumners


4 Answers

Another option is to use the built-in map function:

>>> my_list = [[1,2,3],[4,5,6],[7,8,9]]
>>> neg = lambda x: -x
>>> f = lambda x: map(neg, x)
>>> map(f, my_list)
[[-1, -2, -3], [-4, -5, -6], [-7, -8, -9]]
like image 168
chradcliffe Avatar answered Sep 25 '22 10:09

chradcliffe


Many answers are about creating altered copy of list, but literal meaning of question is about in-place modification of list.

Here is my version of best-of-breed in-place list altering solution:

def alter_elements(lst, func):
  for i, item in enumerate(lst):
    if isinstance(item, list):
      alter_elements(item, func)
    else:
      lst[i] = func(item)

Test run:

>>> sample = [[1,2,3],[4,5,6],[7,8,9]]
>>> alter_elements(sample, lambda x: -x)
>>> print sample
>>> [[-1, -2, -3], [-4, -5, -6], [-7, -8, -9]]

No list copies. No hardcoded bounds. No list comprehensions with side-effects.

like image 34
Constantin Avatar answered Sep 22 '22 10:09

Constantin


It's possible to code a more general solution to this problem. The following works in Python 3.0, regardless of the level of nesting.

Let's define recursive_map:

import collections

def recursive_map(f, iterable):
    for e in iterable:
        if isinstance(e, collections.Iterable):
            yield recursive_map(f, e)
        else:
            yield f(e)

Now, the requested negation function can be coded as follows:

import functools
import operator

negate = functools.partial(recursive_map, operator.neg)

Thus, for some collection of arbitrarily nested iterables x, we calculate its negation y like this:

y = negate(x)

Addendum:

As noted by user chradcliffe, the above negate function yields a generator which may contain other generators which ..., etc. To expand/evaluate all these generators, we need to apply list() to all of them. So we define another general mapping function, this time one that works on the iterables themselves.

def recursive_iter_map(f, iterable):
    def rec(e):
        if isinstance(e, collections.Iterable):
            return recursive_iter_map(f, e)
        else:
            return e

    return f(map(rec, iterable))

Now,

all_lists = functools.partial(recursive_iter_map, list)
y = all_lists(negate(x))

will actually negate every element right away and return the complete list.

Note that we can regard a nested collection of iterables as a tree. Each iterable is a subtree, while non-iterables are leaves. Hence the first function I defined works on leaves, and the second function works on non-leaves.

like image 27
Stephan202 Avatar answered Sep 25 '22 10:09

Stephan202


By "alter" I assume you mean "negate" (but you should have said that).

I notice that you're iterating over each element of a two-dimensional array (a list of lists) and treating each element as a list of three elements... but in fact each element is just a number, in your question as stated. So I would do something like this:

my_list = [[-n for n in l] for l in my_list]
like image 35
David Z Avatar answered Sep 22 '22 10:09

David Z