1

I have the following listin Python 3:

['X', 'X', 'X', 'T', '.', '.', '.', '.', 'O', 'O', '.', '.', '.', '.', '.', '.', 'X', 'O', 'X', 'T', 'X', 'X', 'O', 'O', 'O', 'X', 'O', 'X', 'X', 'X', 'O', 'O', 'X', 'O', 'X', '.', 'O', 'X', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', 'O', 'O', 'X', 'X', 'O', 'X', 'X', 'X', 'O', 'X', '.', 'T', 'O', '.', '.', 'O', 'X', 'X', 'X', 'O', '.', '.', 'O', '.', '.', 'O', '.', '.', 'T', '.', '.', '.', 'O', 'X', 'X', 'X', 'X', 'O', '.', '.', '.', '.', 'O', '.', '.', '.', '.', 'O']

and after 16 places, I want to split it like this:

CASE_1 = ['X', 'X', 'X', 'T', '.', '.', '.', '.', 'O', 'O', '.', '.', '.', '.', '.', '.']
CASE_2 = ['X', 'O', 'X', 'T', 'X', 'X', 'O', 'O', 'O', 'X', 'O', 'X', 'X', 'X', 'O', 'O']
CASE_3 = ['X', 'O', 'X', '.', 'O', 'X', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.']
CASE_4 = ['O', 'O', 'X', 'X', 'O', 'X', 'X', 'X', 'O', 'X', '.', 'T', 'O', '.', '.', 'O'] etc

How do I achieve this? Thanks

LazySloth13
  • 2,369
  • 8
  • 28
  • 37
  • @jamylak agree, it's a duplicate of linked question – David Unric Apr 13 '13 at 09:20
  • @Lewis: I strongly recommend you do use a simpler - and clearly readabeland maintanable - implementation, such as the one I provide. jamylak's rendering may work - but I consider it to be more of a fun way of doing things than usefull in real code. It is not reliable across Python implementations, and hard to understand and maintain. – jsbueno Apr 13 '13 at 10:58
  • 2
    @jsbueno My code is perfectly fine :) Check my comment underneath my answer – jamylak Apr 13 '13 at 11:02
  • Your code is more than twice as slow - that is, f the data set is not big enough so that your way don start getting cache missess all over the place. Scattering the data everywhere just to rejoin it, when all you need are the same chunks is plainly not the best approach. – jsbueno Apr 15 '13 at 01:42

2 Answers2

3
>>> L = ['X', 'X', 'X', 'T', '.', '.', '.', '.', 'O', 'O', '.', '.', '.', '.', '.', '.', 'X', 'O', 'X', 'T', 'X', 'X', 'O', 'O', 'O', 'X', 'O', 'X', 'X', 'X', 'O', 'O', 'X', 'O', 'X', '.', 'O', 'X', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', 'O', 'O', 'X', 'X', 'O', 'X', 'X', 'X', 'O', 'X', '.', 'T', 'O', '.', '.', 'O', 'X', 'X', 'X', 'O', '.', '.', 'O', '.', '.', 'O', '.', '.', 'T', '.', '.', '.', 'O', 'X', 'X', 'X', 'X', 'O', '.', '.', '.', '.', 'O', '.', '.', '.', '.', 'O']
>>> cases = list(zip(*[iter(L)]*16))

I'll just show you that this matches the example by printing it out

>>> import pprint
>>> pprint.pprint(cases, width=400)
[('X', 'X', 'X', 'T', '.', '.', '.', '.', 'O', 'O', '.', '.', '.', '.', '.', '.'),
 ('X', 'O', 'X', 'T', 'X', 'X', 'O', 'O', 'O', 'X', 'O', 'X', 'X', 'X', 'O', 'O'),
 ('X', 'O', 'X', '.', 'O', 'X', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.'),
 ('O', 'O', 'X', 'X', 'O', 'X', 'X', 'X', 'O', 'X', '.', 'T', 'O', '.', '.', 'O'),
 ('X', 'X', 'X', 'O', '.', '.', 'O', '.', '.', 'O', '.', '.', 'T', '.', '.', '.'),
 ('O', 'X', 'X', 'X', 'X', 'O', '.', '.', '.', '.', 'O', '.', '.', '.', '.', 'O')]

As for how this works: How does zip(*[iter(s)]*n) work in Python?

Community
  • 1
  • 1
jamylak
  • 128,818
  • 30
  • 231
  • 230
  • +1, nice answer. But I don't think that `list()` is needed :p. – TerryA Apr 13 '13 at 08:26
  • @Haidro It's redundant on Python 2 but you need it on Python 3 – jamylak Apr 13 '13 at 08:51
  • Ah, my mistake then. Sorry :). – TerryA Apr 13 '13 at 10:05
  • @Haidro It's worth noting for Python 2 users, I forgot to mention it – jamylak Apr 13 '13 at 10:06
  • A nice entry fro the "yearly obfuscated Python context" whent there is one. Sorry about being picky. – jsbueno Apr 13 '13 at 10:42
  • @jsbueno I think it's beautiful! :) – jamylak Apr 13 '13 at 10:43
  • Also, this relies on the itnernal implementation of "zip" which is in no way guaranteed. -1 . – jsbueno Apr 13 '13 at 10:43
  • @jsbueno howso??? This is an accepted python idiom – jamylak Apr 13 '13 at 10:44
  • @jamylak: it is not without fun, indeed - a nice and fun hack - but too unsafe to go into any production code, as it might change between Python versions. Ok. I will remove teh "-1" for the fun. – jsbueno Apr 13 '13 at 10:45
  • @jsbueno Thanks but I am still interested to know what you are saying. Could you please elaborate? – jamylak Apr 13 '13 at 10:46
  • @jamylak: This relies on "zip" picking one - and only one - element of each of the 16 iters it see - (which are the same). If the zip implementation for whatever reason decides tortry to pre-fetch elements, they would get out of order - too say just one example of how it could go wrong. It is complicated and it does rely on non-intuitive behaviors - such as putting the same iter object in different posisions in a list. Such a hack should never be used in production. (And oh dear, the OP just accepeted it, I doubt very much he does understand what is going on, it is just a black box) – jsbueno Apr 13 '13 at 10:54
  • 2
    @jsbueno How is it a hack if it's support by Python devs themselves: http://docs.python.org/2/library/itertools.html#recipes (look for grouper recipe) What you said about not being able to rely on `zip` is wrong. [*"The left-to-right evaluation order of the iterables is guaranteed"*](http://docs.python.org/2/library/functions.html#zip) – jamylak Apr 13 '13 at 10:58
  • @jamylak Oh and it's also listed on the [builtins page](http://docs.python.org/2/library/functions.html#zip) I linked as an example! Admit defeat! :) – jamylak Apr 13 '13 at 11:05
  • `>>> timeit("list(zip(*[iter(x)]* 16) )", "x=range(1600)") 18.835296154022217 >>> timeit("[x[i: i + 100] for i in range(0,1600,100)]", "x=range(1600)") 7.518025159835815 ` - that is besides it being much harder to maintain and event to guess what is going on. – jsbueno Apr 15 '13 at 01:39
  • The fact that someone lieks to mention celver and fun hacks ont he cos doe not, by any way make it the best way to do it. – jsbueno Apr 15 '13 at 01:39
  • @jsbueno huh? Your timings are completely wrong. Try this: `python -m timeit -s "s=range(1600)" "zip(*[iter(s)]*16)"` `10000 loops, best of 3: 32.8 usec per loop` `python -m timeit -s "s=range(1600)" "[s[i: i + 16] for i in range (0,len(s), 16)]"` `10000 loops, best of 3: 44.8 usec per loop` Also if you are using Python 2 the call to `list` is redundant so remove it in your timings. – jamylak Apr 15 '13 at 04:12
  • @jsbueno Also I posted one online as well: `http://ideone.com/sM06Mx`. The `zip` one will be much faster because it executes C code and doesn't have the python overhead of the list comprehension. It also works for any iterable, not just lists, unlike the list comp. So if there should be one way to do it, I vouch for this – jamylak Apr 15 '13 at 04:27
2

"There should be one-- and preferably only one --obvious way to do it."

>>> a = ['X', 'X', 'X', 'T', '.', '.', '.', '.', 'O', 'O', '.', '.', '.', '.', '.', '.', 'X', 'O', 'X', 'T', 'X', 'X', 'O', 'O', 'O', 'X', 'O', 'X', 'X', 'X', 'O', 'O', 'X', 'O', 'X', '.', 'O', 'X', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', 'O', 'O', 'X', 'X', 'O', 'X', 'X', 'X', 'O', 'X', '.', 'T', 'O', '.', '.', 'O', 'X', 'X', 'X', 'O', '.', '.', 'O', '.', '.', 'O', '.', '.', 'T', '.', '.', '.', 'O', 'X', 'X', 'X', 'X', 'O', '.', '.', '.', '.', 'O', '.', '.', '.', '.', 'O']
>>> b = [a[i: i + 16] for i in range(0,len(a), 16)   ]
>>> from pprint import pprint
>>> pprint(b, width=400)
[['X', 'X', 'X', 'T', '.', '.', '.', '.', 'O', 'O', '.', '.', '.', '.', '.', '.'],
 ['X', 'O', 'X', 'T', 'X', 'X', 'O', 'O', 'O', 'X', 'O', 'X', 'X', 'X', 'O', 'O'],
 ['X', 'O', 'X', '.', 'O', 'X', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.'],
 ['O', 'O', 'X', 'X', 'O', 'X', 'X', 'X', 'O', 'X', '.', 'T', 'O', '.', '.', 'O'],
 ['X', 'X', 'X', 'O', '.', '.', 'O', '.', '.', 'O', '.', '.', 'T', '.', '.', '.'],
 ['O', 'X', 'X', 'X', 'X', 'O', '.', '.', '.', '.', 'O', '.', '.', '.', '.', 'O']]
jsbueno
  • 99,910
  • 10
  • 151
  • 209