Kraizand
Kraizand

Reputation: 23

Create list of list where the split point are integers

Let's consider that I have a list like the following one:

myList = [
    '1000', 'ParameterName=Device type', 'ObjectType=0x7', 'DataType=0x7',
    'AccessType=ro', 'PDOMapping=0', 'ObjFlags=1', 'ParameterValue=0x00020192',
    '1001', 'ParameterName=Error register', 'ObjectType=0x7', 'DataType=0x5',
    'AccessType=ro', 'PDOMapping=0', 'ObjFlags=1', 'ParameterValue=0x00',
    '1003', 'SubNumber=6', 'ParameterName=Error history', 'ObjectType=0x8'
]

My split points will be '1000', '1001' and '1003' where the objective is to have

listoflists = [
    ['1000', 'ParameterName=Device type', 'ObjectType=0x7', 'DataType=0x7',
     'AccessType=ro', 'PDOMapping=0', 'ObjFlags=1', 'ParameterValue=0x00020192'],
    ['1001', 'ParameterName=Error register', 'ObjectType=0x7', 'DataType=0x5',
     'AccessType=ro', 'PDOMapping=0', 'ObjFlags=1', 'ParameterValue=0x00'],
    ['1003', 'SubNumber=6', 'ParameterName=Error history', 'ObjectType=0x8']
]

I can easily do it with a simple for loop as follows.

There are some extra checks as I asked for the nominal case but the number can be an hex (so I use is_numeric) and also it can be with a different format such as 4digit+string (1234sub2) so I slice the data. The last check is because as I use the is_digit some hex can be considered data when it is really text but as always that his happens there is an 'DataThatMaybeIsConfusedAsHex = value' I can discriminate using the '='.

for value in configurationFileList:
    if is_numeric(value[:4]) and counter != 0 and "=" not in value:

        # Append the list to the list of lists
        configurationFileListForEachIndex.append(tmpList.copy())

        # Clear the list
        tmpList.clear()

        # Append the New Index
        tmpList.append(value)

    else:
        tmpList.append(value)

    counter += 1

I would like to ask if there is any 'prettier' and more efficient way of doing this.

Upvotes: 2

Views: 118

Answers (6)

Yang Liu
Yang Liu

Reputation: 346

If my understanding serves me, following is the code I think can work:

indexs = [i for i, v in enumerate(myList) if v.isnumeric()]
listoflists = [myList[prev:cur] for prev,cur in zip(indexs,indexs[1:]+[len(myList)])]

Result:

>>> print(listoflists)
[['1000', 'ParameterName=Device type', 'ObjectType=0x7', 'DataType=0x7', 'AccessType=ro', 'PDOMapping=0', 'ObjFlags=1', 'ParameterValue=0x00020192'], ['1001', 'ParameterName=Error register', 'ObjectType=0x7', 'DataType=0x5', 'AccessType=ro', 'PDOMapping=0', 'ObjFlags=1', 'ParameterValue=0x00'], ['1003', 'SubNumber=6', 'ParameterName=Error history', 'ObjectType=0x8']]

Upvotes: 2

 queryable=iter(myList)

 listOfList=[]
 sublist=[]
 for a in queryable:
     if (a.isnumeric()==True) & (len(sublist)>0):
          listOfList.append(sublist)
          sublist=[]
          sublist.append(a) 
     else:
          sublist.append(a) 
    
if(len(sublist)>0):
    listOfList.append(sublist)

print(listOfList)

Upvotes: 0

Mustafa Aydın
Mustafa Aydın

Reputation: 18315

from itertools import takewhile

iterator = iter(myList[1:])
pivots = ["1000", "1001", "1003"]
list_of_lists = [[piv] + list(it.takewhile(lambda entry: entry not in pivots, iterator)) 
                 for piv in pivots]

itertools.takewhile(predicate, iterator), well, takes from the iterator while the predicate is true. In this case predicate is whether the current entry is a pivot or not. In every turn of the list-comprehension, the iterator is getting exhausted i.e. every turn we get a list of the desired list_of_lists. The [1:] slicing when making the iterator from myList is so that takewhile can start taking first pivot's entries at the very beginning.

Upvotes: 0

srishtigarg
srishtigarg

Reputation: 1204

I think this is the prettiest way to do it:

import numpy as np
res_list = [i for i, value in enumerate(myList) if str.isdigit(value) == True]
result = [l.tolist() for l in np.split(myList, res_list)[1:]]

This uses list comprehension and split function, thus does not need any for loop.

Upvotes: 0

Tamercan
Tamercan

Reputation: 1

you may be interested in this solution.

myList = ['1000', 'ParameterName=Device type', 'ObjectType=0x7', 'DataType=0x7', 'AccessType=ro', 'PDOMapping=0',
          'ObjFlags=1', 'ParameterValue=0x00020192', '1001', 'ParameterName=Error register', 'ObjectType=0x7',
          'DataType=0x5', 'AccessType=ro', 'PDOMapping=0', 'ObjFlags=1', 'ParameterValue=0x00', '1003', 'SubNumber=6',
          'ParameterName=Error history', 'ObjectType=0x8']

secondlist = []
secondlist.append(myList)
print(secondlist)

Upvotes: -3

Jack Smith
Jack Smith

Reputation: 336

I think this is a good candidate for an iterator function:

from typing import Iterator, List

def get_chunks(value: List[str]) -> Iterator[List[str]]:
    chunk: List[str] = []
    for item in value:
        if is_int(item):
            # If the value is an integer, we should start a new chunk!
            yield chunk
            chunk = []
        chunk.append(item)
    yield chunk  # Make sure to yield the remaining chunk at the end


def is_int(value: str) -> bool:
    try:
        int(value)
        return True
    except ValueError:
        return False

assert list(get_chunks(["100", "foo", "200", "bar"])) == [[], ["100", "foo"], ["200", "bar"]]

To remove outer empty list when the sequence starts with an integer:

list(filter(None, get_chunks(["100", "foo", "200", "bar"])))
# [["100", "foo"], ["200", "bar"]]

One could easily abstract this over the breakpoint condition function (is_int in this example), making it nicely composable with the various helpers in itertools.

Upvotes: 2

Related Questions