yanman1234
yanman1234

Reputation: 1019

List comprehension, if slice past list bounds do this

I am fairly new to python and am using a list comprehension in the following snippet:

while offset < len(list)
    s = ['{:04x}'.format(i) for i in list[offset:offset+16]]
    do stuff with s
    offset += 16

The snippet loops through the list and adds up to 16 elements to s in a formatted manner, then adds 16 to the offset value to obtain the next 16 until all elements are seen. This works in my unsimplified code, but I'd like to impose a placeholder for when the slice exceeds the list size, instead of having it just stop adding elements to s. I know I can do this with a full for loop but would like to try and do this in comprehension for the sake of keeping this concise to one line. I imagine I would need an if/else in the comprehension but cannot seem to figure out what conditions are needed.

Upvotes: 0

Views: 248

Answers (3)

MSeifert
MSeifert

Reputation: 152725

The easiest way would be to use some sort of grouper that gives you the blocks. The itertools-module presents such a function:

from itertools import zip_longest  # or izip_longest in Python2

def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)

And with that function you can do it like this:

lst = list(range(20))

for group in grouper(lst, n=8, fillvalue=0):
    print(['{:04x}'.format(i) for i in group])

yielding this output:

['0000', '0001', '0002', '0003', '0004', '0005', '0006', '0007']
['0008', '0009', '000a', '000b', '000c', '000d', '000e', '000f']
['0010', '0011', '0012', '0013', '0000', '0000', '0000', '0000']

Upvotes: 0

jsbueno
jsbueno

Reputation: 110486

Concatenate your slice with a list containing placeholders - thus when the slice is over, the placeholders will be fetched, and add to the loop a secondary iterator with the final size you want (a range will do), to limit the yielded items from the first part:

s = ['{:04x}'.format(i) for i, j in in  zip(list[offset:offset+16] + ([placeholder] * 16)  , range(16) ) ]

Upvotes: 0

Elisha
Elisha

Reputation: 23780

You can dynamically pad the sliced chunk to a fixed size. For example:

while offset < len(l):
    s = ['{:04x}'.format(i) for i in l[offset:offset + 16] + [0] * max(0, offset + 16 - len(l))]
    offset += 16
    # Do something

For example with fixed size of 4, this code:

WIDTH = 4
offset = 0
l = list(range(10))
while offset < len(l):
    s = ['{:04x}'.format(i) for i in
         l[offset:offset + WIDTH] + [0] * max(0, offset + WIDTH - len(l))]
    offset += WIDTH
    print(s)

Yields:

['0000', '0001', '0002', '0003']
['0004', '0005', '0006', '0007']
['0008', '0009', '0000', '0000']

Upvotes: 1

Related Questions