user2906838
user2906838

Reputation: 1178

Changing the Duplicate values in the multi dimensional list in Python

I've a list of lists like this:

[[10000, 10001, 10002, 115050, 11506,11786,11787, 11788], [12553,12554,10001,10002], [9500, 9501, 9502, 9503, 9759, 9760, 9761, 11778,11779, 11780, 11781,11782, 12112, 12113]]

What I want to do is, find the duplicate values that are in these lists, and change them by 1 or 2 as required, so that there would be no duplicate or overlap at all. I can't change them to random values, because they are pixel values and changing them hugely will distort my result.

I tried something like this:

even = [[10000, 10001, 10002, 115050, 11506,11786,11787, 11788], [12553,12554,10001,10002], [9500, 9501, 9502, 9503, 9759, 9760, 9761, 11778,11779, 11780, 11781,11782, 11788, 11789, 12112, 12113]]
if(len(even)>1):
        inter = []
        for i in range(len(even)-1):
            #intersections = [set(even[i]).intersection(even[j]) for j in range(i+1, len(even)) ]
            for j in range(i+1, len(even)-1):
                intersect = list(set(even[i]).intersection(even[j]))
                if len(intersect)>0:
                    for inte in intersect:
                        even[j][even[j].index(inte)] =even[j][even[j].index(inte)]+1
                        print("we changed the value here", index)
        print(even)

This is hardly and decent solution, because it fails in this very example. I tried to do it with numpy as well.

length = len(sorted(even,key=len, reverse=True)[0])
y=np.array([xi+[None]*(length-len(xi)) for xi in even])
print( y, even)
y[:,1:] *=(np.diff(y,axis=1)!=0)

However this code would change the duplicate values to 0, which I don't desire. Any help would be really appreciated.

Two things to Note: there shouldn't be any duplicate value in any of the lists inside the list, and the changed value shouldn't have the huge difference, 1,2 + or - depending upon, how we can mitigate the duplication. Thanks in advance.

Upvotes: 4

Views: 684

Answers (2)

piRSquared
piRSquared

Reputation: 294218

You can create a closure and return a function to apply to every element that remembers all elements that have been seen.

def f():
  seen = set()        # Thing that sees all
  def g(a):           # New function that will refer to `seen`
    while a in seen:
      a += 1          # Keep adding one until we find one not in `seen`
    seen.add(a)
    return a
  return g            # Return the function that remembers `seen`

t = f()

even = [[1, 2], [2, 3], [4, 7]]

# not apply `t` ourselves
# neither `t` nor `f` need to know about the structure of `even`
[[t(x) for x in row] for row in even]

[[1, 2], [3, 4], [5, 7]]

We can take this a step further and create a generator that traverses arbitrarily nested lists

from collections.abc import Iterable

def h(i, t=None):

  if t is None:
    t = f()

  for x in i:
    if isinstance(x, Iterable) and not isinstance(x, str):
      yield [*h(x, t)]
    else:
      yield t(x)

Does it work on even?

[*h(even)]

[[1, 2], [3, 4], [5, 7]]

What about something more complicated?

[*h([1, 2, [1, 2], 3, 4, 9, [1, [20, 21, 21]]])]

[1, 2, [3, 4], 5, 6, 9, [7, [20, 21, 22]]]

Upvotes: 3

John Zwinck
John Zwinck

Reputation: 249123

Here's an in-place solution:

def dedupe(lol):
    seen = set()
    for lst in lol:
        for ii, val in enumerate(lst):
            while val in seen:
                val += 1
            lst[ii] = val
            seen.add(val)

And here's a generator which does not modify the input:

def dedupe(lol):
    seen = set()
    for lst in lol:
        new = []
        for val in lst:
            while val in seen:
                val += 1
            new.append(val)
            seen.add(val)
        yield new

You can use it like print(list(dedupe(l)).

Upvotes: 2

Related Questions