How do I stop the function when I have a unique list?

Question:

I tried a function that would remove both adjacent duplicates in a list. The remove any new duplicate pair and the function will keep going until there are no more duplicate pairs in the list.

I ran into the issue of figuring out how to tell the function to stop once I have a list without adjacent duplicates.

def removepair(no):
  i = 1
  if len(no) == 0 or len(no) == 1:
    return no 
  while i < len(no):   
    if no[i] == no[i-1]:
      no.pop(i)
      no.pop(i-1)
      i -= 1  
    i += 1
  return removepair(no)

So far the function will return 0 or single elements after removal:

input: [1, 2, 2, 1] output: []

or

input: [4, 4, 4, 4, 4] output: [4]

But the problem is I don’t know how to stop the recursive function once it has a list with more than 1 element:

input: [1,2,3,3,2,1,5,6,7]
expected output: [5,6,7]

Asked By: user11319914

||

Answers:

We may be able to avoid boolean flags and counters if we set up our recursion carefully:

def removepairs(numbers):

    if not numbers:  # base case #1, empty
        return numbers

    first, *second_on = numbers

    if not second_on:  # base case #2, one element
        return numbers

    second, *third_on = second_on

    if first == second:
        return removepairs(third_on)

    result = [first] + removepairs(second_on)

    if result == numbers:
        return numbers  # base case #3, no change!

    return removepairs(result)

print(removepairs([1, 2, 3, 3, 2, 1, 5, 6, 7]))

OUTPUT

> python3 test.py
[5, 6, 7]
> 
Answered By: cdlane

If recursive function is not a requirement, it can be simply done using the following code. I have commented the print statement.

def removepair(input_list):
    unique_input_list = list(set(input_list))
    output_list = list(x for x in unique_input_list if input_list.count(x)%2 == 1)
    #print('Input List: ', input_list)
    #print('Output list: ', output_list)
    return output_list

Input List:  [1, 2, 3, 3, 2, 1, 5, 6, 7]
Output list:  [5, 6, 7]

Input List:  [4, 4, 4, 4, 4]
Output list:  [4]

Input List:  [1, 2, 3, 3, 2, 1]
Output list:  []
Answered By: Ora Aff

Your recursion should stop when no elements where popped from the list, not when the list is almost empty:

def removepair(no):
    L = len(no)
    if L <= 1:
        return no
    i = 1
    while i < len(no):
        if no[i] == no[i-1]:
            no.pop(i)
            no.pop(i-1)
            i -= 1
        i += 1
    if len(no) < L:
        # elements where popped since the list len has decreased
        return removepair(no)
    else:
        return no

Your code is difficult to understand since it uses a mix of recursion and side effects. Usually, you use either one or the other. Here you can replace your recursive call with a while:

def removepair(no):
    while True:
        L = len(no)
        if L <= 1:
            return no
        i = 1
        while i < len(no):
            if no[i] == no[i-1]:
                no.pop(i)
                no.pop(i-1)
                i -= 1
            i += 1
        if len(no) == L: # no elements where popped
            return no

But it’s not really Pythonic and I think you should not modify the parameter no inside the function but rather return a new list. Why not iterate over the list and do not copy the duplicates in the result?

def removepair(no):
    ret = []
    for e in no:
        if ret and e == ret[-1]: # current element is the same as the last element
            ret.pop()
        else:
            ret.append(e)
    return ret

Or with a fold:

def removepair(no):
    import functools
    return functools.reduce(lambda acc, x: acc[:-1] if acc and acc[-1]==x else acc+[x], no, [])
Answered By: jferard
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.