index out of range in Numbers list - python

I am learning the basics of python, and trying to solve the above mentioned problem. However, I got stuck at this point: when I'm revising the code, everything seems OK to me, but I get an error:
print repr(string), numbers_in_lists(string) == result
File "/tmp/vmuser_tmqtokdyaa/main.py", line 25, in numbers_in_lists
while int(string[i]) <= prev:
IndexError: string index out of range
I did revise other threads with the same IndexError problem, but it didn't quite solve my situation here. Any clues? Thank you in advance.
Here goes the task:
Numbers in lists by SeanMc from forums
define a procedure that takes in a string of numbers from 1-9 and
outputs a list with the following parameters:
Every number in the string should be inserted into the list.
If a number x in the string is less than or equal
to the preceding number y, the number x should be inserted
into a sublist. Continue adding the following numbers to the
sublist until reaching a number z that
is greater than the number y.
Then add this number z to the normal list and continue.
def numbers_in_lists(string):
i=0
prev = 0
list = []
sublist = []
while i< len(string):
if int(string[i]) > prev:
list.append(string[i])
prev = int(string[i])
i +=1
else:
while int(string[i]) <= prev:
sublist.append(string[i])
i +=1
list.append(sublist)
sublist = []
return list
#testcases
string = '543987'
result = [5,[4,3],9,[8,7]]
print repr(string), numbers_in_lists(string) == result
string= '987654321'
result = [9,[8,7,6,5,4,3,2,1]]
print repr(string), numbers_in_lists(string) == result
string = '455532123266'
result = [4, 5, [5, 5, 3, 2, 1, 2, 3, 2], 6, [6]]
print repr(string), numbers_in_lists(string) == result
string = '123456789'
result = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print repr(string), numbers_in_lists(string) == result

That happens because in this inner while, where you get the error, you don't check if i < len(str) and you keep incrementing, checking only condition unrelated to this problem, ignoring if you've reached last character of a string or not.
I would change it to:
def numbers_in_lists(string):
i=0
prev = 0
list = []
sublist = []
while i< len(string):
if int(string[i]) > prev:
list.append(string[i])
prev = int(string[i])
i +=1
else:
while i< len(string) and int(string[i]) <= prev:
sublist.append(string[i])
i +=1
list.append(sublist)
sublist = []
return list
And I would consider using for loop instead of while in your case, but above works anyway.

Related

finding index[] of odd values in a list of integers?

I am trying to find the index value of some odd integers in a list. The program at its current stage only returns a new list containing only the odd integers.
image of my code
This program returns the values [3, 5, 7]. How would i be able to retrieve the index of these odd values instead? For example, [0, 1, 3, 5] for the list shown above.
I have tried "position = dataItem1.index()". I understand that i would need to input an index value within the index brackets.
you can use also use enumerate : it returns the index of the element and the element (also, in python you can loop directly over the elements of a list, no need of while loops). I also encourage the use of .append() to add an element to a list, this is clearly more efficient than concatenate two lists as you do:
l = [0, 3, 2, 3, 4, 7, 6]
def get_odds_index(l):
res = []
for idx, val in enumerate(l):
if val % 2 != 0:
res.append(idx)
return res
get_odds_index(l)
Instead of adding the element on that index in the odd list, just add index
Like this
def positionOfOdds(arr):
odds = []
length = len(arr)
index = 0
while index < length:
data = arr[index]
if data % 2 != 0:
odds.append(index)
index += 1
return odds
you already know the index, it is the loop variable
def positionOfOdds(arr):
odds = []
length = len(arr)
index = 0
while index < length:
data = arr[index]
if data % 2 != 0:
odds = odds + [index]
index += 1
return odds
You use an array called odds to store the odd numbers, to get the indexes you can create instead an array to store the indexes of the odd numbers, which I called odds_idxs. It should look something like this:
num_list = [0, 3, 2, 3, 4, 7, 6]
def position_of_odds(num_list):
odds_idxs = []
length = len(num_list)
index = 0
while index < length:
if num_list[index] % 2 != 0:
odds_idxs = odds_idxs + [index]
index = index + 1
return odds_idxs

Unable to return a list in Python

I am trying a question from leetcode which is like this.
Given an array of integer nums and an integer target, return indices of the two numbers such that they add up to the target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
You can return the answer in any order.
Example 1:
Input: nums = [2,7,11,15], target = 9
Output: [0,1]
Explanation: Because nums[0] + nums[1] == 9, we return [0, 1].
My solution is like this:
def xyz(list_1, result):
dict_1 = dict()
for i in range(len(list_1)):
num = list_1[i]
compliment = result - num
print(compliment)
if num in dict_1:
print(dict_1)
# breakpoint()
# abc = [i,dict_1[compliment]]
return [i, dict_1[compliment]]
else:
dict_1[compliment] = i
print(dict_1)
list_1 = [2, 3, 6, 8, 9, 11]
result = 5
final = xyz(list_1, result)
print(final)
But when I run the program, I get the following error:
File "E:/Leetcode_Python/sumtarget.py", line 19, in xyz
return [i, dict_1[compliment]]
KeyError: 2
I dont understand why I cannot return the list. What is wrong here??
Updates:
def xyz(list_1,result):
dict_1 = dict()
for i in range(len(list_1)):
num = list_1[i]
compliment = result - num
# print(compliment)
if compliment in dict_1:
print(compliment)
print(dict_1[compliment])
return [i,dict_1[compliment]]
else:
dict_1[num] = i
# print(dict_1)
list_1 = [2,3,6,8,9,11]
result = 19
final = xyz(list_1,result)
print(final)
I tried with the above code and it worked. I am surprised. I just started looking for compliment and start adding number. But isnt it the same as adding compliment and start looking for num??
If compliment is not found, then you're supposed to insert num into the dictionary
Instead of checking for num, you need to check the occurrence of compliment in the dictionary.
def xyz(list_1, result):
dict_1 = dict()
for i in range(len(list_1)):
num = list_1[i]
compliment = result - num
if compliment in dict_1:
return [i, dict_1[compliment]]
else:
dict_1[num] = i
list_1 = [2, 3, 6, 8, 9, 11]
result = 5
final = xyz(list_1, result)
print(final)
Edit: this approach is easier to understand; adding num and then searching for compliment. However, like the comment suggests, it can also be done the other way round, like the OP has attempted. The issue is return [i, dict_1[compliment]], which should be replaced by return [i, dict_1[num]].

Finding numbers that cannot be paired

I've a function which aim is to find numbers that you cannot pair with others. If a number cannot be paired up with another identical number, then you will have to return it.
example:
[1,1,1,1, 2, 3, 4]
expected result:
[2,3,4]
def pair(input):
while len(input) > 2:
for i in range(len(input) - 1):
for j in range(len(input) - 1):
print(input)
if input[i] == input[j]:
input.pop(i)
input.pop(j)
print(input)
else:
break
return input
print(pair([1,1,1,1, 2, 3, 4]))
If you can use a library, you could use a Counter from collections, and just return the values in the list that have an odd count (since that implies there is one of those numbers that cannot be paired with another):
from collections import Counter
ll = [ 1, 2, 1, 3, 2, 3, 1, 4, 1, 2, 3, 4 ]
c = Counter(ll)
[num for num, cnt in c.items() if cnt % 2 == 1]
Output:
[2, 3]
One way to solve this problem is to find the unique elements in the list, and if an element occurs an odd number of times the in the list, returned it.
def pair(lst):
set_lst = set(lst)
output = []
for elem in set_lst:
if lst.count(elem) % 2 != 0:
output.append(elem)
return output
print(pair([1, 1, 1, 1, 2, 3, 4]))
Keeping track of the numbers that occurred an odd number of times:
def pair(lst):
odd = set()
for x in lst:
odd ^= {x}
return list(odd)
print(pair([1,1,1,1, 2, 3, 4]))
According your example input list is sorted. In this case #KetZoomer's solution is not effective, because it searching for the elem in whole list and counting it every time (lst.count(elem))
If you counted number of 1's you don't need to search for 2 in whole list from 0th element to last element again.
You should to use something like this:
def pairs(lst: list) -> list:
if len(lst) > 0:
# set first element
current_el = lst[0]
duplicate_count = 1
values_without_pair = []
else:
return []
# loop for elements starting from 2nd
for i in range(1, len(lst)):
# if looped element same as stored in memory increase count and go to next element
if current_el == lst[i]:
duplicate_count += 1
# if element is changed then check count value is even or not
# and set new element for checking
else:
if duplicate_count % 2 != 0:
values_without_pair.append(current_el)
duplicate_count = 1
current_el = lst[i]
# process last element
if duplicate_count % 2 != 0:
values_without_pair.append(current_el)
return values_without_pair
print(pairs([1,1,1,1,2,3,4]))

How to get a symmetrical sub list and then get the sum of that sub list?

What the code does: Takes a Python list of integers as input and searches for a 'symmetrical' inner-portion of the list then it takes that inner portion and gets the sum of it.
Symmetry occurs if the value of the ith element from the start of the list is equal to the value of the ith element from the end of the list.
Examples of what i want:
symmetrical_sum([10,11,12,11,12]) == ([11, 12, 11], 34)
symmetrical_sum([9,99,88,8,77,7,77,8,88,10,100]) == ([88, 8, 77, 7, 77, 8, 88], 353)
symmetrical_sum([10,8,7,5,9,8,15]) == ([8, 7, 5, 9, 8], 37)
Is there any short-coded solution to get the outputs in the examples given above? I have a correct coded version but it is more than 30 lines of code and would like to know if there is shorter way.
def symmetrical_sum(a):
dup=[x for n, x in enumerate(a) if x in a[:n]] #to get the duplicate
to_int = int(''.join(map(str,dup))) #change duplicate into int
dup1_index=a.index(to_int) #index the first duplicate
dup2_index=a.index(to_int,dup1_index+1) #index the second duplicate
portion=a[dup1_index:dup2_index+1] #get the symetric portion
total = sum(portion) #sum the elements in portion
tuple1 = (portion,total) #create tuple
return tuple1
You can do this with a recursive function as follows:
def sym(L):
if L[0] == L[-1]:
lis, sum_list = L, sum(L)
answer = f'inner-portion: {lis}, sum: {sum_list}'
return answer
else:
return sym(L[1:-1])
Note that the above will work for a list input with odd number items (that is the length is an odd number)and a single item list but not an empty list. A more wholistic approach is the following:
def symmetrical_sum(a):
index_from_start = 0
index_from_end= len(a)-1
sym_list= []
while index_from_start<=index_from_end:
if a[index_from_start] == a[index_from_end]:
sym_list= a[index_from_start:index_from_end+1]
break
else:
index_from_start += 1
index_from_end -= 1
return sym_list, sum(sym_list)
QED :)
Try using numpy:
get a list of booleans representing the condition if ith element from beginning and end are equal
find indices where the boolean values are True
The min value represent where symmetry starts and max value is where it ends, so slice the list array to get the "symmetrical" sub-list
return the new list and its sum as a tuple
Following code should work for the input and output you posted:
import numpy as np
def sym_sum(arr):
l = len(arr)-1
bool_arr = np.array([x==arr[l-i] for i,x in enumerate(arr)])
idx_arr = np.where(bool_arr==True)[0]
if len(idx_arr):
res = arr[min(idx_arr):max(idx_arr)+1]
else:
res = []
return (res, sum(res))
If you need actual symmetrical output use:
import numpy as np
def get_sym(arr):
l = len(arr) - 1
bool_arr = np.array([x == arr[l - i] for i, x in enumerate(arr)])
idx_arr = np.where(bool_arr == False)[0]
if len(idx_arr):
return get_sym(arr[min(idx_arr)+1:max(idx_arr)])
else:
return (arr, sum(arr))
Here we recursively call the function until the unsymmetrical portions are completely striped.
In pure python this can be achieved like this:
def symmetrical_sum(a):
inner_portion = []
sum = 0;
start = 0;
for i in a:
end = len(a) - (start + 1);
if a[start] == a[end]:
inner_portion = a[start:(end+1)];
for i in inner_portion:
sum+= i
break
start+= 1
return (inner_portion, sum)
print(symmetrical_sum([10,11,12,11,12])) #([11, 12, 11], 34)
Mid = float(len(a))/2
my_list =a[int(Mid - .5): int(Mid + .5)]
my_list_total =sum(my_list)
get_total= (my_list,my_list_total)
answer return get_total ([5], 5)

Python: parsing a string of concatenated ascending integers

The objective is to parse the output of an ill-behaving program which concatenates a list of numbers, e.g., 3, 4, 5, into a string "345", without any non-number separating the numbers. I also know that the list is sorted in ascending order.
I came up with the following solution which reconstructs the list from a string:
a = '3456781015203040'
numlist = []
numlist.append(int(a[0]))
i = 1
while True:
j = 1
while True:
if int(a[i:i+j]) <= numlist[-1]:
j = j + 1
else:
numlist.append(int(a[i:i+j]))
i = i + j
break
if i >= len(a):
break
This works, but I have a feeling that the solution reflects too much the fact that I have been trained in Pascal, decades ago. Is there a better or more pythonic way to do it?
I am aware that the problem is ill-posed, i.e., I could start with '34' as the initial element and get a different solution (or possibly end up with remaining trailing numeral characters which don't form the next element of the list).
This finds solutions for all possible initial number lengths:
a = '3456781015203040'
def numbers(a,n):
current_num, i = 0, 0
while True:
while i+n <= len(a) and int(a[i:i+n]) <= current_num:
n += 1
if i+n <= len(a):
current_num = int(a[i:i+n])
yield current_num
i += n
else:
return
for n in range(1,len(a)):
l = list(numbers(a,n))
# print only solutions that use up all digits of a
if ''.join(map(str,l)) == a:
print(l)
[3, 4, 5, 6, 7, 8, 10, 15, 20, 30, 40]
[34, 56, 78, 101, 520, 3040]
[34567, 81015, 203040]
little modification which allows to parse "7000000000001" data and give the best output (max list size)
a = 30000001
def numbers(a,n):
current_num, i = 0, 0
while True:
while i+n <= len(a) and int(a[i:i+n]) <= current_num:n += 1
if i+2*n>len(a):current_num = int(a[i:]);yield current_num; return
elif i+n <= len(a):current_num = int(a[i:i+n]);yield current_num;i += n
else: return
print(current_num)
for n in range(1,len(a)):
l = list(numbers(a,n))
if "".join(map(str,l)) == a:print (l)

Categories