Home > other >  Replace None with incremental value excluding values from skip list
Replace None with incremental value excluding values from skip list

Time:06-07

I am trying to replace all None values in all sublists within a list to incremental numbers starting from 0 but excluding the numbers from the skip list. And there is one more requirement. If first element of a sublist matches to any other sublists' first element, then they all need to have the same value that replaces None in them if present. This is what I could try so far.

skip = [1,2]
a = [[1, None, 2], [3, 4, 5], [1, None, 7], [8, 9, 10],[11, None, 12]]
b = 0
d = {}
for i in range(len(a)):
    if a[i][1]==None:
        if b in skip:
            print("found b in skip")
            b = b   1
        if a[i][1] in d.keys():
            a[i][1] = d[a[i][1]]
        else:
            a[i][1] = b
        d[a[i][0]] = b
        b = b   1
print(d)
print(a)

Output:

found b in skip
{1: 2, 11: 3}
[[1, 0, 2], [3, 4, 5], [1, 2, 7], [8, 9, 10], [11, 3, 12]]

Expected output:

[[1, 0, 2], [3, 4, 5], [1, 0, 7], [8, 9, 10], [11, 3, 12]]

CodePudding user response:

You're looking up the wrong element in the cache in a couple places, and not skipping properly when the skip list contains consecutive elements. Here's the minimal fix with inline comments indicating changed lines:

skip = [1,2]
a = [[1, None, 2], [3, 4, 5], [1, None, 7], [8, 9, 10],[11, None, 12]]
b = 0
d = {}
for i in range(len(a)):
    if a[i][1]==None:
        while b in skip:          # Loop until skipped all elements in skip
            print("found b in skip")
            b = b   1
        if a[i][0] in d.keys():   # Check for first, not second element
            a[i][1] = d[a[i][0]]  # Use first element, not second, for lookup
        else:
            a[i][1] = b
            d[a[i][0]] = b  # Indent so we only set cached value on cache miss
            b = b   1       # Indent so we only increment b on new first element
print(d)
print(a)

Try it online!

And here's a more heavily modified version that is somewhat more Pythonic, using names, not indexing (when possible):

skip = {1,2}  # Use set instead of list; if skip is large, list membership check will be expensive, set will stay cheap
a = [[1, None, 2], [3, 4, 5], [1, None, 7], [8, 9, 10],[11, None, 12]]
b = 0
d = {}
for sublist in a:                      # Loop over values instead of indexing a over and over, C-style (slow and less readable)
    first, middle, _ = sublist         # Unpack to useful names (reducing risk of misindexing, and making more readable code)
                                       # Not unpacking in for loop itself because we need to reassign elements of sublist
    if middle is None:                 # Always use is/is not to compare to None
        while b in skip:
            b  = 1                     # Use  = to avoid repeating variable name
        if first in d:                 # No need for .keys(); "in d" has same effect as "in d.keys()" and avoids creating unnecessary keys view
            sublist[1] = d[first]      # Indexing needed for assignment to modify original list
        else:
            sublist[1] = d[first] = b  # Can populate cache and reassign middle value all at once
            b  = 1
print(d)
print(a)

Try it online!

Either version gets the expected output from the question.

CodePudding user response:

Here is my approach to solve this, It's lengthy but easy to follow. This considers if None is the first item or last item as well, not just the middle item. So it's a little bit more general. I've put comments for explanation:

skip = {1, 2}
lst = [[1, None, 2], [3, 4, 5], [1, None, 7], [8, 9, 10], [11, None, 12]]

# Filling a dictionary which groups sublists based on first item.
d = {}
for sublist in lst:
    first, mid, last = sublist
    if first is None:  # No need to add to the dictionary because later, we won't traverse if the first item is  None.
        continue
    if first in d:
        d[first].append(sublist)
    else:
        d[first] = [sublist]


# A generator which yields correct next value
def increment_counter():
    counter = 0
    while True:
        if counter not in skip:
            yield counter
        counter  = 1


counter = increment_counter()

for sublist in lst:
    if all(item is not None for item in sublist):
        continue

    first, middle, last = sublist

    if first is None:  # No check is needed if None is the first item.
        sublist[0] = next(counter)

    else:  # Either middle or last item is None. We need to travers our dictionary.
        n = next(counter)
        for l in d[first]:
            for i, item in enumerate(l):
                if item is None:
                    l[i] = n

print(lst)

output:

[[1, 0, 2], [3, 4, 5], [1, 0, 7], [8, 9, 10], [11, 3, 12]]

This also works for lists like:

[[1, None, 2], [3, 4, 5], [1, None, 7], [8, 9, 10], [11, 12, None], [None, 12, 5]]
  • Related