Why is this simple Python program not giving the correct output?
Question:
The challenge is given below:
You will be given an array of numbers. You have to sort the odd numbers in ascending order while leaving the even numbers at their original positions.
[7, 1] => [1, 7]
[5, 8, 6, 3, 4] => [3, 8, 6, 5, 4]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0] => [1, 8, 3, 6, 5, 4, 7, 2, 9, 0]
My code’s logic: The function takes a source array (src_arr
). I create a new array of only the odd numbers and sort them in odd_arr
. In the for-loop, the if-statement checks each whether each number in src_arr
is odd and replaces it with a corresponding sorted odd number from the odd_arr
.
def sort_array(src_arr):
odd_arr = sorted([num for num in src_arr if num%2 != 0])
i = 0
for num in src_arr:
if num%2 != 0:
src_arr[src_arr.index(num)] = odd_arr[i]
i += 1
return src_arr
print(sort_array([5, 3, 2, 8, 1, 4]))
The input is [5, 3, 2, 8, 1, 4]
and output of this should be [1, 3, 2, 8, 5, 4]
. But I keep getting the output which is exactly same as the input. I did a pdb.trace()
and everything works fine till it comes to the number 1 in the input list, and I cannot figure out why it is not giving the correct output.
Answers:
The line "src_arr[src_arr.index(num)] = odd_arr[i]"
may not always replace the correct element in src_arr with the corresponding odd number from odd_arr
.
Try this:
def sort_array(src_arr):
odd_arr = sorted([num for num in src_arr if num % 2 != 0])
i = 0
for j, num in enumerate(src_arr):
if num % 2 != 0:
src_arr[j] = odd_arr[i]
i += 1
return src_arr
One of the other ways to look is you are changing the original array, so the number 1 has two indexes after you replace 5 with 1
Simply if you have a copy and change the copy while keeping the original array intact will solve your issue
def sort_array(src_arr):
odd_arr = sorted([num for num in src_arr if num%2 != 0])
new_arr = src_arr[:]
i = 0
for num in src_arr:
if num%2 != 0:
new_arr[src_arr.index(num)] = odd_arr[i]
i += 1
return new_arr
print(sort_array([5, 3, 2, 8, 1, 4]))
You chose to run the for
-loop on src_arr
and maintain a manual index i
over odd_arr
.
Personally I find it much more straightforward to run the for
-loop on odd_arr
and maintain a manual index i
over src_arr
.
This is because we want to look at every element of odd_arr
in order, so a simple straightforward for
-loop is appropriate; but src_arr
is the array on which we’re skipping elements, so it’s easier to maintain our own index i
and skip the elements we want to skip manually.
def sort_array(src_arr):
odd_arr = sorted(num for num in src_arr if num % 2 != 0)
i = 0
for x in odd_arr:
while src_arr[i] % 2 == 0: # skip even numbers in src_arr
i += 1
src_arr[i] = x
i += 1
return src_arr
print(sort_array([5, 3, 2, 8, 1, 4]))
# [1, 3, 2, 8, 5, 4]
You can even simplify the code a bit by making odd_arr
an iterator on the sorted list of odd numbers, and simply calling next(odd_arr)
when needed, without manually managing the index:
def sort_array(src_arr):
odd_arr = iter(sorted([num for num in src_arr if num%2 != 0]))
for idx, num in enumerate(src_arr):
if num%2 != 0:
src_arr[idx] = next(odd_arr)
return src_arr
print(sort_array([5, 3, 2, 8, 1, 4]))
# [1, 3, 2, 8, 5, 4]
There’re already some great answers posted. Here is just one more alternative – for fun. IT’s inspired by earlier comments too.
def sort_odd(A):
# generator expression to extract odd numbers first
odds = sorted(x for x in A if x & 1)[::-1]
# List Comp to achieve:
# replace odd num. one by one, leave evens unchanged
return [x if not x&1 else odds.pop() for x in A]
print(sort_odd(L))
The challenge is given below:
You will be given an array of numbers. You have to sort the odd numbers in ascending order while leaving the even numbers at their original positions.
[7, 1] => [1, 7]
[5, 8, 6, 3, 4] => [3, 8, 6, 5, 4]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0] => [1, 8, 3, 6, 5, 4, 7, 2, 9, 0]
My code’s logic: The function takes a source array (src_arr
). I create a new array of only the odd numbers and sort them in odd_arr
. In the for-loop, the if-statement checks each whether each number in src_arr
is odd and replaces it with a corresponding sorted odd number from the odd_arr
.
def sort_array(src_arr):
odd_arr = sorted([num for num in src_arr if num%2 != 0])
i = 0
for num in src_arr:
if num%2 != 0:
src_arr[src_arr.index(num)] = odd_arr[i]
i += 1
return src_arr
print(sort_array([5, 3, 2, 8, 1, 4]))
The input is [5, 3, 2, 8, 1, 4]
and output of this should be [1, 3, 2, 8, 5, 4]
. But I keep getting the output which is exactly same as the input. I did a pdb.trace()
and everything works fine till it comes to the number 1 in the input list, and I cannot figure out why it is not giving the correct output.
The line "src_arr[src_arr.index(num)] = odd_arr[i]"
may not always replace the correct element in src_arr with the corresponding odd number from odd_arr
.
Try this:
def sort_array(src_arr):
odd_arr = sorted([num for num in src_arr if num % 2 != 0])
i = 0
for j, num in enumerate(src_arr):
if num % 2 != 0:
src_arr[j] = odd_arr[i]
i += 1
return src_arr
One of the other ways to look is you are changing the original array, so the number 1 has two indexes after you replace 5 with 1
Simply if you have a copy and change the copy while keeping the original array intact will solve your issue
def sort_array(src_arr):
odd_arr = sorted([num for num in src_arr if num%2 != 0])
new_arr = src_arr[:]
i = 0
for num in src_arr:
if num%2 != 0:
new_arr[src_arr.index(num)] = odd_arr[i]
i += 1
return new_arr
print(sort_array([5, 3, 2, 8, 1, 4]))
You chose to run the for
-loop on src_arr
and maintain a manual index i
over odd_arr
.
Personally I find it much more straightforward to run the for
-loop on odd_arr
and maintain a manual index i
over src_arr
.
This is because we want to look at every element of odd_arr
in order, so a simple straightforward for
-loop is appropriate; but src_arr
is the array on which we’re skipping elements, so it’s easier to maintain our own index i
and skip the elements we want to skip manually.
def sort_array(src_arr):
odd_arr = sorted(num for num in src_arr if num % 2 != 0)
i = 0
for x in odd_arr:
while src_arr[i] % 2 == 0: # skip even numbers in src_arr
i += 1
src_arr[i] = x
i += 1
return src_arr
print(sort_array([5, 3, 2, 8, 1, 4]))
# [1, 3, 2, 8, 5, 4]
You can even simplify the code a bit by making odd_arr
an iterator on the sorted list of odd numbers, and simply calling next(odd_arr)
when needed, without manually managing the index:
def sort_array(src_arr):
odd_arr = iter(sorted([num for num in src_arr if num%2 != 0]))
for idx, num in enumerate(src_arr):
if num%2 != 0:
src_arr[idx] = next(odd_arr)
return src_arr
print(sort_array([5, 3, 2, 8, 1, 4]))
# [1, 3, 2, 8, 5, 4]
There’re already some great answers posted. Here is just one more alternative – for fun. IT’s inspired by earlier comments too.
def sort_odd(A):
# generator expression to extract odd numbers first
odds = sorted(x for x in A if x & 1)[::-1]
# List Comp to achieve:
# replace odd num. one by one, leave evens unchanged
return [x if not x&1 else odds.pop() for x in A]
print(sort_odd(L))