2

Problem to solve: Define a Python function remdup(l) that takes a non-empty list of integers l and removes all duplicates in l, keeping only the last occurrence of each number. For instance: if we pass this argument then remdup([3,1,3,5]) it should give us a result [1,3,5]

def remdup(l):
    for last in reversed(l):
        pos=l.index(last)
        for search in reversed(l[pos]):
            if search==last:
                l.remove(search)

    print(l)


remdup([3,5,7,5,3,7,10])

# intended output [5, 3, 7, 10]

On line 4 for loop I want the reverse function to check for each number excluding index[last] but if I use the way I did in the above code it takes the value at pos, not the index number. How can I solve this

KGB
  • 23
  • 4

7 Answers7

1

You need to reverse the entire slice, not merely one element:

        for search in reversed(l[:pos]):

Note that you will likely run into a problem for modifying a list while iterating. See here


It took me a few minutes to figure out the clunky logic. Instead, you need the rest of the list:

    for search in reversed(l[pos+1:]):

Output:

[5, 3, 7, 10]
Prune
  • 76,765
  • 14
  • 60
  • 81
  • It is taking the last element anyway – KGB Feb 06 '21 at 02:23
  • It worked and also It is more implementable with my code. Thank You. Really appreciate that – KGB Feb 06 '21 at 02:37
  • That's good to hear. However, *please* look at the link for iterating and deleting, and consider converting your logic. What you're doing is a bit touchy, even though you're using a temporary value for the list. – Prune Feb 06 '21 at 02:38
  • Hmmm. Okay. I will definitely take a loot at it. – KGB Feb 06 '21 at 02:41
1

Your original algorithm could be improved. The nested loop leads to some unnecessary complexity.

Alternatively, you can do this:

def remdup(l):
seen = set()
for i in reversed(l):
    if i in seen:
        l.remove(i)
    else:
        seen.add(i)

print(l)

I use the 'seen' set to keep track of the numbers that have already appeared.

However, this would be more efficient:

def remdup(l):
seen = set()
for i in range(len(l)-1, -1, -1):
    if l[i] in seen:
        del l[i]
    else:
        seen.add(l[i])

print(l)

In the second algorithm, we are iterating over the list in reverse order using a range, and then we delete any item that already exists in 'seen'. I'm not sure what the implementation of reversed() and remove() is, so I can't say what the exact impact on time/space complexity is. However, it is clear to see exactly what is happening in the second algorithm, so I would say that it is a safer option.

0

This is a fairly inefficient way of accomplishing this:

def remdup(l):
    i = 0
    while i < len(l):
        v = l[i]
        scan = i + 1
        while scan < len(l):
            if l[scan] == v:
                l.remove(v)
                scan -= 1
                i -= 1
            scan += 1
        i += 1

l = [3,5,7,5,3,7,10]
remdup(l)
print(l)

It essentially walks through the list (indexed by i). For each element, it scans forward in the list for a match, and for each match it finds, it removes the original element. Since removing an element shifts the indices, it adjusts both its indices accordingly before continuing.

It takes advantage of the built-in the list.remove: "Remove the first item from the list whose value is equal to x."

dkamins
  • 21,450
  • 7
  • 55
  • 59
0

Here is another solution, iterating backward and popping the index of a previously encountered item:

def remdup(l):
    visited= []
    for i in range(len(l)-1, -1, -1):
        if l[i] in visited:
            l.pop(i)
        else:
            visited.append(l[i])
    print(l)

remdup([3,5,7,5,3,7,10])
#[5, 3, 7, 10]
pakpe
  • 5,391
  • 2
  • 8
  • 23
0

Using dictionary:

def remdup(ar):
    d = {}
    for i, v in enumerate(ar):
        d[v] = i
    return [pair[0] for pair in sorted(d.items(), key=lambda x: x[1])]


if __name__ == "__main__":
    test_case = [3, 1, 3, 5]
    output = remdup(test_case)
    expected_output = [1, 3, 5]
    assert output == expected_output, f"Error in {test_case}"

    test_case = [3, 5, 7, 5, 3, 7, 10]
    output = remdup(test_case)
    expected_output = [5, 3, 7, 10]
    assert output == expected_output, f"Error in {test_case}"

Explanation

  • Keep the last index of each occurrence of the numbers in a dictionary. So, we store like: dict[number] = last_occurrence
  • Sort the dictionary by values and use list comprehension to make a new list from the keys of the dictionary.
arshovon
  • 13,270
  • 9
  • 51
  • 69
0

Along with other right answers, here's one more.

from iteration_utilities import unique_everseen,duplicates
import numpy as np
list1=[3,5,7,5,3,7,10]
dup=np.sort(list((duplicates(list1))))
list2=list1.copy()
for j,i in enumerate(list2):
    try:
        if dup[j]==i:
           list1.remove(dup[j])
    except:
        break
print(list1)
Nag Gooty
  • 26
  • 1
0

How about this one-liner: (convert to a function is easy enough for an exercise)

# - one-liner Version

lst = [3,5,7,5,3,7,10]

>>>list(dict.fromkeys(reversed(lst)))[::-1]

# [5, 3, 7, 10]             

if you don't want a new list, you can do this instead:

lst[:] = list(dict.fromkeys(reversed(lst)))[::-1]
Daniel Hao
  • 4,922
  • 3
  • 10
  • 23