pythonsublist

Substitution of values from one list with corresponding index from another


I need a little bit of help. I'm stuck on how to approach this one. So the general idea is to have a function taking 3 inputs. First is a list, second is also a list, but it contains values which are considered to be indexes and third is one value. The goal is to check if value in second list matches any index in first list and in case it does it should replace it by the value of third parameter.

I have something like this, but it doesn't work properly.

def sub(original, replace, new_value):
    for item in replace:

        for index in range(len(original)):

            if original[index] == item:
               original[index] = new_value

    return original

Examples of how it is supped to look:

sub([1, 2, 3, 4, 5], [2, 3], 'H') -> [1, 2, 'H', 'H', 5]
sub([1, 2, 3, 4, 5], [0, 10], 50) -> [50, 2, 3, 4, 5]

My output is like this

[1, 'a', 'a', 4, 5]

Solution

  • Here's the most obvious way to do this:

    def sub(original, replace, new_value):
        for index in range(len(original)):
            if index in replace:
                original[index] = new_value
    
        return original
    

    However, the test for index being in replace is linear, which is suboptimal. It's preferable to create a set up front, then use the set for the membership test:

    def sub(original, replace, new_value):
        replace_set = set(replace)
    
        for index in range(len(original)):
            if index in replace_set:
                original[index] = new_value
    
        return original
    

    These both produce the desired result. The second will be faster for long lists.

    But assuming that most of the indices in replace are within range, the following will probably be faster. It needs to make a range check to skip indices in replace that are out of range, but it only needs to iterate over replace:

    def sub(original, replace, new_value):
        original_len = len(original)
    
        for index in replace:
            if 0 <= index < original_len:
                original[index] = new_value
    
        return original
    

    Note that the above solutions are all "destructive", in that they modify the list passed in through original. If you don't want to modify that list, you can use a list comprehension to create a new list, as follows:

    def sub(original, replace, new_value):
        replace_set = set(replace)
    
        return [new_value if ix in replace_set else v
                for ix, v in enumerate(original)]
    

    The returned list is still the desired result, but the original list is unchanged.