1

I have some multiple dimension array like this:

a= [[1,2],[2,4],[31,2]]

b= [[[1,2],[2,4],[31,2]],[[22,34],[322,323],[3454,544]]]

c= [[[[1,2],[2,4],[31,2]],[[22,34],[322,323],[3454,544]]],[[[1,2],[2,4],[31,2]],[[22,34],[322,323],[3454,544]]]]

Now I want to change the value of each [x,y] pair to [x,y-x], desired result:

a= [[1,0],[2,2],[31,-29]]    ==> [1,0] =  [1,(1-0)]

I tried to use generator like this(Inspired from this answer):

def flatten(ary):
    for el in ary:
        if isinstance(el, int):
            yield ary
        else:
            for sub in flatten(el):
                yield sub

But it does not work as expected.

How to fix it?

Note:

The operation which transform [x,y] to [x,y-x] may be changed accordingly, for example

[x,y] ==> [x,x*y] maybe another operation.

So I do not want to hard code the operation to the iteration.

I want something like this:

for x,y in flatten(ary):
  return x,y-x

Then if necessary, I just change it to :

for x,y in flatten(ary):
  return x,y+x  # any operation I want
Community
  • 1
  • 1
hguser
  • 35,079
  • 54
  • 159
  • 293

4 Answers4

1

Here's one approach - it's a recursive function where at each level you find if you have a list of lists, and if so recurse deeper. If you have a list of elements, perform the (x, y) = (x, y-x) transformation.

a= [[1,2],[2,4],[31,2]]

b= [[[3,3],[22,542]]]

c= [[[[1,33],[5,88]]]]

def flatten(l):
    for i, e in enumerate(l):
        if type(e) is list and type(e[0]) is list:
            flatten(e)
        else:
            x, y = e
            l[i] = (x, y-x)

>>> flatten(a)
[(1, 1), (2, 2), (31, -29)]
>>> flatten(b)
[[(3, 0), (22, 520)]]
>>> flatten(c)
[[[(1, 31), (5, 78)]]]
Martin Konecny
  • 57,827
  • 19
  • 139
  • 159
  • This the recursion solution, I wonder if it is possible to use the generator? – hguser Jun 19 '14 at 03:06
  • I don't think a generator solution would work here - what elements would you want returned on each iteration? Would it be the root level list (only one iteration), or would it be each "leaf" list (multiple iterations but then they won't be nested in other lists)? – Martin Konecny Jun 19 '14 at 03:10
  • The deepest element should be returned is the coordinate pair like [x,y]. – hguser Jun 19 '14 at 03:12
  • Just to clarify, in the flatten solution you linked to, they are provided a flattened list which works well with iterating over a generator. On the other hand, you want your original list's structure kept intact, which conflicts with the generator. – Martin Konecny Jun 19 '14 at 03:12
  • The link you provided actually does use recursion. If you want the output to be a flat list, then you can use a generator. – Martin Konecny Jun 19 '14 at 03:15
  • Thanks for your explanation, in fact I just want to separate the operation from the iteration. I update my question, can you have a check? – hguser Jun 19 '14 at 03:17
1

Using funcy:

>>> from funcy import chunks, iflatten
>>> a= [[1,2],[2,4],[31,2]]
>>>
>>> b= [[[3,3],[22,542]]]
>>>
>>> c= [[[[1,33],[5,88]]]]
>>> chunks(2, iflatten(a))
[[1, 2], [2, 4], [31, 2]]
>>> chunks(2, iflatten(b))
[[3, 3], [22, 542]]
>>> chunks(2, iflatten(c))
[[1, 33], [5, 88]]

With this you can do:

>>> for x, y in chunks(2, iflatten(any)):
...     # your operations
James Mills
  • 18,669
  • 3
  • 49
  • 62
  • You can also use iterator version of `chunks` here - [ichunks](http://funcy.readthedocs.org/en/latest/seqs.html#ichunks). And if you are sure you have even number of items it's better to use [ipartition](http://funcy.readthedocs.org/en/latest/seqs.html#ipartition). – Suor Jul 16 '14 at 08:46
1

I just improve Martin Konecny answer, below the code

def flatten(array, operation):

    for i, e in enumerate(array):
        if isinstance(e, (list, tuple)) and isinstance(e[0], (list, tuple)):
            flatten(e, operation)
        elif isinstance(e, (int, float)):
            array[0], array[1] = operation(array[0], array[1])
            break
        else:
            array[i] = operation(e[0], e[1])

a= [[1,2],[2,4],[31,2]]

b= [[[1,2],[2,4],[31,2]],[[22,34],[322,323],[3454,544]]]

c= [[[[1,2],[2,4],[31,2]],[[22,34],[322,323],[3454,544]]],[[[1,2],[2,4],[31,2]],[[22,34],[322,323],[3454,544]]]]

and the result of the demo:

>>> 
>>> flatten(a, lambda x, y: [x, y * x])
>>> a
[[1, 2], [2, 8], [31, 62]]
>>> flatten(b, lambda x, y: [x, y - x])
>>> b
[[[1, 1], [2, 2], [31, -29]], [[22, 12], [322, 1], [3454, -2910]]]
>>> flatten(c, lambda x, y: [x, y + x])
>>> c
[[[[1, 3], [2, 6], [31, 33]], [[22, 56], [322, 645], [3454, 3998]]], [[[1, 3], [2, 6], [31, 33]], [[22, 56], [322, 645], [3454, 3998]]]]
>>> 

This is to new test:

>>> 
>>> d = [1,2]
>>> flatten(d, lambda x, y: [x, y +5])
>>> d
[1, 7]
>>> 

Hopes, can help you.

Tok Soegiharto
  • 329
  • 1
  • 8
0

Another way to do it using funcy. Just stop flattening at appropriate level:

from funcy import iflatten, is_list

follow = lambda l: is_list(l) and not is_list(l[0])
d = [(x, y - x) for x, y in iflatten(l, follow)]
Suor
  • 2,845
  • 1
  • 22
  • 28