Python: split list of integers based on step between them
Asked Answered
P

5

6

I have the following problem. Having a list of integers, I want to split it, into a list of lists, whenever the step between two elements of the original input list is not 1. For example: input = [0, 1, 3, 5, 6, 7], output = [[0, 1], [3], [5, 6, 7]]

I wrote the following function, but it's uggly as hell, and I was wondering if anyone of you guys would help me get a nicer solution. I tried to use itertools, but couldn't solve it.

Here's my solution:

def _get_parts(list_of_indices):
    lv = list_of_indices
    tuples = zip(lv[:-1], lv[1:])
    split_values = []
    for i in tuples:
        if i[1] - i[0] != 1:
            split_values.append(i[1])
    string = '/'.join([str(i) for i in lv])
    substrings = []
    for i in split_values:
        part = string.split(str(i))
        substrings.append(part[0])
        string = string.lstrip(part[0])
    substrings.append(string)
    result = []
    for i in substrings:
        i = i.rstrip('/')
        result.append([int(n) for n in i.split('/')])
    return result

Thanks a lot!

Pyretic answered 22/2, 2013 at 8:34 Comment(0)
P
9

This works with any iterable

>>> from itertools import groupby, count
>>> inp = [0, 1, 3, 5, 6, 7]
>>> [list(g) for k, g in groupby(inp, key=lambda i,j=count(): i-next(j))]
[[0, 1], [3], [5, 6, 7]]
Peculation answered 22/2, 2013 at 8:40 Comment(1)
Nice solution. I think description will be usefull: j=count() creates a counter. Every call to next(j) will return int step by 1. Not obvious python behaviour: default value for function argument is created once at function creation. So, j will be initialized with count() only once, on next call to key, arg j will have instance previously created. groupby will append to iterable g all items from inp, that have same key value. If key value has changed - new g is created. For items from inp: item=0, key=0-0=0; item=1, key=1-1=0; item=3, key=3-2=1; item=5, key=5-3=2 and so on.Maidenhead
C
2
def _get_parts(i, step=1):
    o = []
    for x in i:
        if o and o[-1] and x - step == o[-1][-1]:
            o[-1].append(x)
        else:
            o.append([x])
    return o

_get_parts([0, 1, 3, 5, 6, 7], step=1)
# [[0, 1], [3], [5, 6, 7]])
Carlsen answered 22/2, 2013 at 8:38 Comment(0)
D
0

Here is a solution utilizing a for loop.

def splitbystep(alist):
  newlist = [[alist[0]]]
  for i in range(1,len(alist)):
    if alist[i] - alist[i-1] == 1:
      newlist[-1].append(alist[i])
    else:
      newlist.append([alist[i]])
  return newlist
Duodecimo answered 22/2, 2013 at 8:42 Comment(0)
S
0

This is how I'd do it:

inp = [0, 1, 3, 5, 6, 7]
base = []

for item in inp:
    if not base or item - base[-1][-1] != 1: # If base is empty (first item) or diff isn't 1
        base.append([item])                  # Append a new list containing just one item
    else:
        base[-1].append(item)                # Otherwise, add current item to the last stored list in base
print base                                   # => [[0, 1], [3], [5, 6, 7]]
Segno answered 22/2, 2013 at 8:44 Comment(0)
A
0

This is the textbook use case for function split_when from module more_itertools:

import more_itertools

print(list(more_itertools.split_when([0, 1, 3, 5, 6, 7], lambda x,y: y-x != 1)))
# [[0, 1], [3], [5, 6, 7]]

Or, even more simple with more_itertools.consecutive_groups:

print([list(g) for g in more_itertools.consecutive_groups([0, 1, 3, 5, 6, 7])])
# [[0, 1], [3], [5, 6, 7]]
Annunciator answered 16/9, 2021 at 10:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.