Python Code Challenges: Lists (Advanced): Double Index

Hello there! I’m new here :slightly_smiling_face:
For Python Code Challenges: Lists (Advanced): Double Index

We are given the below solution
def double_index(my_list, index):

Checks to see if index is too big

if index >= len(my_list):
return my_list
else:
# Gets the original list up to index
my_new_list = my_list[0:index]

Adds double the value at index to the new list

my_new_list.append(my_list[index]*2)

Adds the rest of the original list

my_new_list = my_new_list + my_list[index+1:]
return my_new_list

for
if index >= len(my_list):
return my_list

I think that it should be:
if index>len(my_list)-1
return

Can anyone help me to confirm or explain to me the difference? Thank you!!

You wouldn’t be wrong, but manually doing the heavy lifting with the offset that would normally be handled by the >= operator. Essentially, both yield the same result so we cannot say you are incorrect in any way. However, if you can save some logic by using an operator that does the job, let the operator do the work and skip the addition (an additional step). When effeciency matters (not now) the operator is more efficient.

if index >= len(my_list):

We notice you are using slicing, appending and concatenation, which is a lot of steps for such a low demand operation. All we really need to do in this function is validate the index (check that it is in range), and when valid, mutate the value at that index, then return the list.

n = len(my_list)
if -n <= index < n:
    my_list[index] *= 2

return my_list

To illustrate, considering that Python reads negative indices as right to left, as opposed to the ‘normal’ left to right, the range can include negative numbers.

def double_index(my_list, index):
    n = len(my_list)
    if -n <= index < n:
        my_list[index] *= 2
    return my_list
>>> alist = [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> double_index(alist, 8)
[1, 2, 3, 4, 5, 6, 7, 8, 18]
>>> double_index(alist, -1)
[1, 2, 3, 4, 5, 6, 7, 8, 18]
>>> double_index(alist, -9)
[2, 2, 3, 4, 5, 6, 7, 8, 9]
>>> double_index(alist, 0)
[2, 2, 3, 4, 5, 6, 7, 8, 9]
>>> double_index(alist, 9)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> 

On the whole, it doesn’t get much easier than that. Simple is always better, especially if it works correctly, all the time.


Extra study

>>> alist = [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> for x in range(-9, 9):
        alist = double_index(alist, x)    #  preserve the mutated list


>>> alist
    [4, 8, 12, 16, 20, 24, 28, 32, 36]
>>> 

Because no exceptions are thrown, that means every value in our range is valid. The values in the initial list are all doubled twice, as a result. To repeat, the range() object is valid as pertains to the length of alist, proving our code model, itself, valid.

Bottom line, test, test, test. Test all your ideas and test them again. Compare iterations as you chalk in new changes, and how they test out. Our above model is a reduction to the least number of moving parts and simplest logic. It was with your model that we could derive this reduction. Now you’ve seen it done, once, continue along what ever line of thinking you begin with for each new idea.

Don’t try to write this reduced code from scratch as given in the above form. Write all kinds of code to fulfill your ideas, and then break it down, try refactoring in a small way, test again, refactor some more, test, test, test. One should not try to change the way one thinks, only learn how to reduce and refactor one’s ideas.

If we are always looking in our code for weakness, excess, repetition, uncertainty, we will find it (or not, toward the final iteration). Learning is about mitigation of our findings. It’s not the code that needs honing. It’s our intuition and knowledge assimilation. No limits on the mind, no limits on the potential. It is your potential you seek, not anyone else’s. No limits.

1 Like

Extra study, redux

>>> alist = [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> n = len(alist)
>>> r = range(-n, n)
>>> for x in r:
        alist = double_index(alist, x)


>>> alist
    [4, 8, 12, 16, 20, 24, 28, 32, 36]
>>> 

It may be considered that a range() object is an iterator, though it does not behave completely like an iterator so one would expect it generates an iterator each time we call upon it. r above is a preserved range. It can be polled more than once without being consumed, as an iterator or generator object would be. Enough said. A range object is an iterable, for all intents and purposes.