-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathStringCalculator.py
More file actions
192 lines (140 loc) · 5.13 KB
/
StringCalculator.py
File metadata and controls
192 lines (140 loc) · 5.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#Technical Interview - String Calculator
# Matthew Fyfe, Feb 2021
##############################################################################
# Bonus 4: using regular expressions makes multi-delimiter case easier
import re
# Part 1: simple string calculator
def Add(numbers: str):
'''Add together a string of delimiter separated integers.
Takes in an string of delimiter separated numbers. Adds them together.
This program naively assumes the input is in the correct format.
Ex input: '1,2,3' or '//;\n1;3;4'
:param str numbers: a delimiter separated string of integers
:raise: Exception if one of the integers < 0
:return: an integer value represetning the sum of the inputs
:rtype: int
'''
multiDelim_flag = False
# Part 1: check for empty string
if not numbers:
return 0
# Part 3: Check for custom delimiter
numbers = extractCustomDelimter(numbers)
# We expect a tuple if there was a custom delimiter
if (type(numbers) is tuple):
delimiter = numbers[1]
numbers = numbers[0]
# Bonus 3: handle multi delimter case
if(type(delimiter) is list):
delimiterString = ""
for x in delimiter:
# TODO: should insert escape characters into x to handle '$$' case
# Just handling naive case for now (put a \ at start)
delimiterString += '\\' +str(x) + '|'
# Remove the last '|'
delimiterString = delimiterString[:-1]
multiDelim_flag = True
# Part 1: Default to ',' if not custom
else:
delimiter = ','
# Part 2: Handle new line characters
numbers = removeNewLines(numbers)
# Part 1: Split the delimter seperated values (, if not specified)
# Use map to cast each value into integers, then sum the values
# Part 4: Also verify there are no negatives numbers in the input
# Note: must save map results as list, otherwise it expires after single use
if(not multiDelim_flag):
result = list(map(int, numbers.split(delimiter)))
else:
result = list(map(int, re.split(delimiterString, numbers)))
# Bonus 1: ignore large numbers
result = sum(ignoreTooBig(noNegatives(result)))
# Part 1: return integer result
return result;
# Part 2: handle new line characters in the input string
def removeNewLines(text: str):
''' Strip out newline characters from a string
:param str text: the input text to be modified
:return: the modified string without newline characters
:rtype: str
'''
return text.replace('\n', '')
# Part 3: support custom delimiter
def extractCustomDelimter(text: str):
''' Check for and identify the custom delimiter
:param str text: input string to be scanned for delimters
:return: a tuple containing both the input text without the delimiter
code and the extracted delimiters
:rtype: tuple
'''
#check for // code, if we don't find one assume this is a non-custom case
if(text.find('//') != 0):
return text
#extract delimter...
#expected format: “//[delimiter]\n[delimiter separated numbers]”
firstPosition = text.find('//')
secondPosition = text.find('\n')
delimiter = text[firstPosition+2:secondPosition]
#extract input text
trimmedText = text[secondPosition::]
#return delimiter and trimmed numbers text as tuple
# Bonus 3: check for multiple delimiters
# Assume ',' is always the delimiter delimiter :)
multiDelimiter = delimiter.split(',')
if(len(multiDelimiter) > 1):
return(trimmedText, multiDelimiter)
else:
return(trimmedText, delimiter)
# Part 4: check for negative numbers
def noNegatives(numbers: list):
''' Check the input list for negatives numbers, throw exception if found.
:param list numbers: the input list to be scanned
:raise: Exception if a negative number is found
:return: the unmodified list from our input
:rtype: list
'''
negativesList = []
for x in numbers:
if x < 0:
negativesList.append(x)
# Raise an exception if we found any negative numbers in the input
if(len(negativesList) > 0):
raise Exception("Negatives not allowed" + str(negativesList))
# Otherwise, just return the values
return numbers
# Bonus 1: ignore numbers larger than 1000 from input
def ignoreTooBig(numbers: list):
''' Set values > 1000 to 0 so that they are ignored during sum.
:param list numbers: the input list to be scanned
:return: the potentially modified list from our input
:rtype: list
'''
tooBigList = []
# Find values over 1000
for x in numbers:
if x > 1000:
tooBigList.append(x)
# Remove the bad values from our input list
for y in tooBigList:
numbers.remove(y)
return numbers
##############################################################################
# Run the tests for the program
#Part 1
print("1,2,5 = " + str(Add("1,2,5")))
print("[empty_string] = " + str(Add("")))
#Part 2
print("1\\n,2,3 = " + str(Add("1\n,2,3")))
print("1,\\n2,4 = " + str(Add("1,\n2,4")))
#Part 3
print("//;\\n1;3;4 = " + str(Add("//;\n1;3;4")))
#Part 4 (causes exception, as intended)
#print("// \n1 -2 -3 = " + str(Add("// \n1 -2 -3")))
#Bonus 1
print("2,1001 = " + str(Add("2,1001")))
#Bonus 2
print("//***\\n1***2***3 = " + str(Add("//***\n1***2***3")))
#Bonus 3
print("//$,@\\n1$2@3 = " + str(Add("//$,@\n1$2@3")))
#Bonus 4 (with limitation, dont use multiple characters that must be escaped)
print("//$ ,@\\n4$ 2@3 = " + str(Add("//$ ,@\n4$ 2@3")))