Small Python Challenge No. 1

I bet I already wrote that sometime earlier, but I found myself today writing the following function:

def blocks(seq, block_len):
    """blocks(range(5),2) -> [[0, 1], [2, 3], [4]]"""
    seq_len = len(seq)
    if seq_len%block_len == 0:
        num_blocks = seq_len/block_len
    else:
        num_blocks = 1 + (seq_len/block_len)
    result = [[] for i in xrange(num_blocks)]
    for idx, obj in enumerate(seq):
        result[idx/block_len].append(obj)
    return result

I am not satisfied with this implementation.
The challenge is to write it in a more elegant and short manner. Functions from Python’s standard modules are considered valid solutions.

Ready? GO!

This entry was posted in Challenges, Programming, Python and tagged , . Bookmark the permalink.

13 Responses to Small Python Challenge No. 1

  1. tyoc says:

    Im learning python and I can say I love it in the figurative sense…

    Here is my try:


    import math

    def blocks(seq, subLen):
    """blocks(range(5), 2) -> [[0,1], [2,3], [4])

    Basically get a slice of len = subLen and append until end skipping
    current slice
    """
    initialPosOfSlice = 0
    result = []
    for elementSlice in range(int(math.ceil(len(seq)/float(subLen)))):
    result.append(seq[initialPosOfSlice:initialPosOfSlice+subLen])
    initialPosOfSlice = initialPosOfSlice+subLen
    return result

    print blocks(range(5), 2)

  2. Pingback: Tleyotl Ocelocoatl - tyoc » Una pequeña prueba para lo que he aprendido…

  3. lorg says:

    I am aware that Python code doesn’t come out looking good on this blog, I will try to fix it in the following days. In the meantime, use the pre tag and preview your post, if you intend to post code.

    Regarding the code: personally I don’t like the use of floating point division. At the least it feels like cheating :) At worst it can lead to strange results when depending on floats to yield specific ints. In any case, your solution is valid.

    I’m still wondering if an elegant one-liner is possible…

  4. tyoc says:

    hehe, I will take the idea of challenges, also I have take the idea of time roundup.

    I guess what you mean by “floating point… ckeating”, the problem was how to determine the “end” ceil do the work and the conversion back forward intfloat do the job.

    Here is a second try… ceiling is only “importan”, because you sometimes will need +1, but using a little more the slices…

    def blocks3(seq, subLen):
    “”"blocks(range(5), 2) -> [[0,1], [2,3], [4])

    The same operation over slices, but we use generators instead,
    and an extra if clause for break before start adding []
    “”"
    if subLen == 0:
    subLen = 1
    #sliceSubIndex = ((i,i+subLen)for i in range(0, len(seq), subLen))
    #sliceResult = (seq[i:f] for i,f in sliceSubIndex)
    sliceResult = (seq[i:i+subLen] for i in range(0, len(seq), subLen))
    result = []
    for ele in sliceResult:
    result.append(ele)
    return result

    print “blocks3″, blocks3(range(7), 3)
    print “blocks3″, blocks3(range(5), 0)

    (by the way, I dont see how to edit the anterior post for put pre tags, also I not see a preview comment.)

  5. tyoc says:

    Sorry for the double answer, but little time after, return this 1 liner ;) if you forget the check for the step of length zero

    * turn ou that you dont whant to return the generator, but a generator can be “executed without the loop if you use list(generatorObject)

    def blocks4(seq, subLen=1):
    “”"blocks(range(5), 2) -> [[0,1], [2,3], [4])

    The same operation over slices, but we use generators instead,
    and an extra if for check step not zero
    “”"
    if subLen == 0:
    subLen = 1
    return list((seq[i:i+subLen] for i in range(0, len(seq), subLen)))

  6. Paddy3118 says:

    The following uses groupby which is great *if* you can remember how to use it:

    >>> def blocks(seq, block_len):
    … from itertools import groupby
    … return [ list(z[1]) for z in groupby(range(len(seq)), lambda y: y / 2)]

    >>> blocks(range(5),2)
    [[0, 1], [2, 3], [4]]
    >>>

    - Paddy.

  7. Paddy3118 says:

    Whoops,
    replace the 2 by block_len

    P.S. the pre tag did not work in my comment.

    - Paddy.

  8. lorg says:

    Thanks for the info on the Python problem. This really bugs me, in comments as well as in posts.

    Regarding usage of groupby: Erez once wrote a function named classify_to_dict which was similar to groupby, but returned a dict. It was one of the more useful utility functions I encountered. Later on we found out about groupby, but still classify_to_dict was easier to use and understand.

    Paddy: Your last solution however suffers from a flaw – try blocks(“abcdefg”,3) and see what I mean.
    Tyoc: I really like your last solution, it has the right smell :). You can however remove the double parenthesis, yielding the following line:
    return list(seq[i:i+subLen] for i in range(0, len(seq), subLen))
    Or just list comprehension, list this [seq[i:i+subLen] for i in range(0, len(seq), subLen)].

  9. Paddy3118 says:

    I was not sure if you wanted members of the original sequence back or not. (You could help by making the function comment more complete).

    Here is another go:

    >>> def blocks(seq, block_len):
    … from itertools import groupby
    … return [ [j[1] for j in z[1]] for z in groupby(enumerate(seq), lambda y: y[0] / block_len)]

    >>> blocks(“abcdefg”,3)
    [['a', 'b', 'c'], ['d', 'e', 'f'], ['g']]
    >>> blocks(range(5),2)
    [[0, 1], [2, 3], [4]]
    >>>

    - Paddy.

  10. Erez says:

    I also wrote classify_to_list, which could be more fitting for paddy’s first solution.

  11. Navtej Singh says:

    another onliner for the same problem but by using only __buitins__ (no import)

    >>> def blocks(seq,block_len):
    … return [seq[i:i+block_len] for i in range(0,len(seq),block_len)]

    >>> blocks(range(5),2)
    [[0, 1], [2, 3], [4]]
    >>> blocks(range(6),2)
    [[0, 1], [2, 3], [4, 5]]

    However there are no checks. the inside range would fail for 0.

  12. Navtej Singh says:

    aa haan saw it bit late tyoc has the similar solution

  13. eliben says:

    import itertools

    def split_subsequences(iterable, length=2, overlap=0):
    it = iter(iterable)
    results = list(itertools.islice(it, length))
    while len(results) == length:
    yield results
    results = results[length - overlap:]
    results.extend(itertools.islice(it, length – overlap))
    if results:
    yield results

    if __name__ == ‘__main__’:
    seq = ‘foobarbazer’
    for length in (3, 4):
    for overlap in (0, 1):
    print ‘%d %d: %s’ % (length, overlap,
    map(”.join, split_subsequences(seq, length, overlap)))