Repository: TheAlgorithms/Python

Issue:
improvement to insertion_sort algorithm
### Feature description

I was about to make a PR to improve the implementation of [insertion_sort algorithm](https://github.com/TheAlgorithms/Python/blob/master/sorts/insertion_sort.py) but since there might be multiple ways of doing so, I thought I should first ask your opinions.

These are the things that need improvements:

1. We unnecessarily create a whole new copy of the list: `enumerate(collection[1:])`.

   We can either use "indexes" to avoid this which is not very pythonic, or we can use the iterator of the list using `iter()` and throw away the first item using `next()`. In second case we have to either check for empty list first or wrap it in a try-except block. I'll go with indexes if you ask. What do you think?

2. I think a function should either mutate the list in-place and returns `None`, or it should create new sorted list without modifying the original list. Mutating the list and returning the mutated list is not what most developers expect to see. What do you think?

3. We can safely remove `if insert_index != temp_index:` condition and unindent its body. Assigning an item to an index of a list is not costly. So it's one less line in general.
improvement to insertion_sort algorithm
### Feature description

I was about to make a PR to improve the implementation of [insertion_sort algorithm](https://github.com/TheAlgorithms/Python/blob/master/sorts/insertion_sort.py) but since there might be multiple ways of doing so, I thought I should first ask your opinions.

These are the things that need improvements:

1. We unnecessarily create a whole new copy of the list: `enumerate(collection[1:])`.

   We can either use "indexes" to avoid this which is not very pythonic, or we can use the iterator of the list using `iter()` and throw away the first item using `next()`. In second case we have to either check for empty list first or wrap it in a try-except block. I'll go with indexes if you ask. What do you think?

2. I think a function should either mutate the list in-place and returns `None`, or it should create new sorted list without modifying the original list. Mutating the list and returning the mutated list is not what most developers expect to see. What do you think?

3. We can safely remove `if insert_index != temp_index:` condition and unindent its body. Assigning an item to an index of a list is not costly. So it's one less line in general.


Discussion:
 using enumerate adds unnecessary extra conditions.  'for' loop along with two pointers for comparing and swapping is the most favorable.  I think you should approach with indexes
 using enumerate adds unnecessary extra conditions.  'for' loop along with two pointers for comparing and swapping is the most favorable.  I think you should approach with indexes

Resolving patch:
diff --git a/sorts/insertion_sort.py b/sorts/insertion_sort.py
--- a/sorts/insertion_sort.py
+++ b/sorts/insertion_sort.py
@@ -13,8 +13,19 @@
 python3 insertion_sort.py
 """
 
+from collections.abc import MutableSequence
+from typing import Any, Protocol, TypeVar
 
-def insertion_sort(collection: list) -> list:
+
+class Comparable(Protocol):
+    def __lt__(self, other: Any, /) -> bool:
+        ...
+
+
+T = TypeVar("T", bound=Comparable)
+
+
+def insertion_sort(collection: MutableSequence[T]) -> MutableSequence[T]:
     """A pure Python implementation of the insertion sort algorithm
 
     :param collection: some mutable ordered collection with heterogeneous
@@ -40,13 +51,12 @@ def insertion_sort(collection: list) -> list:
     True
     """
 
-    for insert_index, insert_value in enumerate(collection[1:]):
-        temp_index = insert_index
-        while insert_index >= 0 and insert_value < collection[insert_index]:
-            collection[insert_index + 1] = collection[insert_index]
+    for insert_index in range(1, len(collection)):
+        insert_value = collection[insert_index]
+        while insert_index > 0 and insert_value < collection[insert_index - 1]:
+            collection[insert_index] = collection[insert_index - 1]
             insert_index -= 1
-        if insert_index != temp_index:
-            collection[insert_index + 1] = insert_value
+        collection[insert_index] = insert_value
     return collection
 
 


Test patch: