Search code examples
pythonlistelement

Combine n elements in first half of list with elements in other half of a list, if number of elements in a list is greater than 2


I have a problem with dealing with elements in a list. To be precise in a list of lists. For example, I have list of elements that are read from a file:

list_1 = [['void', None], ['uint8', 'f_MbistTestType_u8'], ['uint8', 'uint32', 'f_MbistTestType_u8', 'f_chip_id_u32'], ['void', None], ['void', None], ['void', None], ['void', None]]

In this case third element has more than two elements. I want to switch element 2 with element 3. So it would look like this:

list_1[2] = ['uint8', 'f_MbistTestType_u8', 'uint32', 'f_chip_id_u32']

If there would be 6 elements i.e.

list_example = ['uint8', 'uint32', 'void', 'f_chip_id_u32', 'f_MbistTestType_u8', None]

After the operation it should look like this:

list_example_sorted = ['uint8', 'f_chip_id_u32', 'uint32', 'f_MbistTestType_u8', 'void', None]

Right now I know how to get those elements in case I have only one occurrence of more than 2 elements, but don't know how to switch their places and also what to do in case I have more than one occurrence:

for elements in list_1:
print(elements)
if len(elements) > 2:
    list_el = elements
    print(list_el)

I tried to pop them out and append, but it won't scale well with more than 4 elements.

I tried to use swap function, but it seems that it doesn't work or I used it wrong?

enter image description here


Solution

  • Going by an input of [1, 1, 1, 2, 2, 2] with the desired output [1, 2, 1, 2, 1, 2], i.e. you want the first element of the left half followed by the first element of the right half and so forth. To make it more obvious:

    • input = [1, 2, 3, 4, 5, 6]
    • output = [1, 4, 2, 5, 3, 6]

    Define a function combine_inplace that combines the ith element of the left half with the ith element of the right half of l:

    def combine_inplace(l):
        mid = len(l) // 2
        ptr = 0
        for left, right in zip(l[:mid], l[mid:]):
            l[ptr], l[ptr+1] = left, right
            # Increment pointer ptr by 2 for the next combination
            ptr += 2
    
    • combine_inplace mutates the passed list l
    • left half and right half are created using slice operator
    • use zip to iterate over both list
    • increment ptr by 2 to get to the next list indices for l

    If you don't want to mutate the list itself you can instead create a new list combined that is returned by the function combine:

    def combine(l):
        mid = len(l) // 2
        combined = []
        for left, right in zip(l[:mid], l[mid:]):
            combined.extend((left, right))
        return combined
    
    • Does not mutate the passed list l
    • Initialise empty list combined to store the combined values
    • use zip to iterate over both list halves
    • Returns the list combined

    This uses the same logic as combine_inplace but you keep the original list intact.

    Both functions combine the elements of the left half with the right half of a given list. The only difference is that with combine you have to store the returned list in a variable to access it.

    >> l = [1, 1, 1, 2, 2, 2]
    >> combine_inplace(l)
    >> print(l)
    [1, 2, 1, 2, 1, 2]
    >> input_list = [1, 2, 3, 4, 5, 6]
    >> output_list = combine(input_list)
    >> print(output_list)
    [1, 4, 2, 5, 3, 6]
    

    Now using either combine or combine_inplace to combine elements of lists with a length > 2 inside a list:

    ll = [[1, 2], [1, 2], [1, 1, 2, 2], [1, 2], [1, 2, 3, 4, 5, 6]]
    # Non-destructive way using combine to create a new list comb_ll
    comb_ll = []
    for el in ll:
        if len(el) > 2:
            el = combine(el)
        comb_ll.append(el)
    
    # Mutates the original list
    for i in range(len(ll)):
        if len(ll[i]) > 2:
            combine_inplace(ll[i])
    

    In both cases you'll get the same result:

    >> print(comb_ll)
    [[1, 2], [1, 2], [1, 2, 1, 2], [1, 2], [1, 4, 2, 5, 3, 6]]
    >> print(ll)
    [[1, 2], [1, 2], [1, 2, 1, 2], [1, 2], [1, 4, 2, 5, 3, 6]]