Created
January 17, 2016 17:51
-
-
Save AurelioDeRosa/abe9cb4e1cb64269cdea to your computer and use it in GitHub Desktop.
Sum stings representing numbers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Exercise: | |
* | |
* Given the string representations of a variable number of integers, | |
* write a function that returns the string representation of the sum of | |
* those integers. The function must return the correct result even | |
* if the sum of the numbers exceeds the maximum number allowed | |
* (Number.MAX_VALUE). In addition, it must work with negative integers. | |
*/ | |
/** | |
* Sums a variable number of stings representing numbers | |
* | |
* @param {string[]} [strings] The strings to sum | |
* | |
* @return {string} | |
*/ | |
function sumStrings() { | |
// The idea of this function is to turn each string | |
// provided into an array of single-character strings | |
// representing digits, and sum them. The function | |
// calculates the final result by summing the strings | |
// two at a time. | |
// | |
// Some special considerations are made for the sum | |
// of negative numbers and a possible carry from | |
// the sum of the single digits of the numbers. | |
/** | |
* Sums two stings representing numbers | |
* | |
* @param {string} a The first string | |
* @param {string} b The second string | |
* | |
* @return {string} | |
*/ | |
function sum(a, b) { | |
// Calculate the sign of the numbers and stores | |
// it as an integer. If the number is positive or | |
// zero the value stored is 1; otherwise it's -1. | |
// | |
// The sign of the numbers is crucial for the sum | |
// of the individual digits. If the sign of the | |
// numbers is different, the algorithm has to | |
// perform a subtraction instead of an addition. | |
var aSign = +a >= 0 ? 1 : -1; | |
var bSign = +b >= 0 ? 1 : -1; | |
// Based on the signs of the numbers, the operation | |
// will be changed. This is important because if the | |
// operation to perform is a subtraction, there are | |
// slightly harder cases to manage. | |
// | |
// For example if the first number is negative and | |
// the second positive with an absolute value greater | |
// than the first, there are more considerations | |
// and adjustments to make before proceeding with the | |
// operation. | |
if (aSign < 0) { | |
// If both numbers are negative, the result is equal | |
// to the opposite (or additive inverse) of the sum | |
// of the numbers. | |
// | |
// If the first number is negative and the second is | |
// positive, the addition can be transformed into | |
// a subtraction. A special case of this operation is | |
// represented by the example described above for which | |
// a further adjustment is performed. | |
if (bSign < 0) { | |
return '-' + sum(a.replace('-', ''), b.replace('-', '')); | |
} else { | |
return sum(b, a); | |
} | |
} | |
// Remove the sign from the numbers (if present) | |
// and convert them into reversed arrays. | |
// This change makes the sum of the numbers easier | |
// because they will be aligned starting from | |
// the last digit. | |
// | |
// Examples: | |
// "145" is turned into ["5", "4", "1"] | |
// "-39" is turned into ["9", "3"] | |
var aArr = a.replace('-', '') | |
.split('') | |
.reverse(); | |
var bArr = b.replace('-', '') | |
.split('') | |
.reverse(); | |
// The variable in which the carry of the individual | |
// sums is stored. This is needed for a correct | |
// calculation of the result. | |
// | |
// Examples: | |
// 5 + 9 results in a carry of 1 | |
// 3 + 2 results in a carry of 0 | |
var carry = 0; | |
// When calculating a sum between a positive and | |
// a negative number, the operation performed | |
// is actually a difference. This operation | |
// requires that a number smaller than the one to | |
// subtract "borrows" a unit from the next digit, | |
// if such digit exists. | |
// | |
// Examples: | |
// 35 - 28 | |
// The first calculation is | |
// 5 - 8 | |
// which is turned into | |
// 15 - 8 | |
// that gives 7. | |
// The second and last operation is | |
// 2 - 2 | |
var decimalCarry = 0; | |
// Determine when to stop the sums by calculating | |
// the number with most digits. | |
var maxLength = Math.max(aArr.length, bArr.length); | |
// The array in which the results of the individual | |
// sums are stored. | |
var result = []; | |
for (var i = 0; i < maxLength; i++) { | |
// If a decimal carry was used in the previous | |
// operation between two digits, reset it to zero | |
// and decrease by the current digit because it | |
// "borrowed" a unit. | |
if (decimalCarry === 10) { | |
decimalCarry = 0; | |
aArr[i]--; | |
} | |
// Determine if the current digit of the first number | |
// needs to "land" one unit. | |
if (aArr[i] < bArr[i] && aArr[i + 1] !== undefined) { | |
decimalCarry = 10; | |
} | |
// Calculate the sum between the two digits taking | |
// into account any carry from a previous sum and | |
// the decimal carry. | |
var digitsSum = decimalCarry + (+aArr[i] || 0) + (bSign * (+bArr[i] || 0)) + carry; | |
// If the sum is less than zero, the second number | |
// is negative. Therefore, to avoid complex branching | |
// of the algorithm, the signs of both the numbers | |
// are reverted. Because of this operation, the result | |
// is reverted as well (by prepending the minus sign). | |
if (digitsSum < 0) { | |
if (aSign < 0) { | |
return '-' + sum(b.replace('-', ''), a.replace('-', '')); | |
} else { | |
return '-' + sum(b.replace('-', ''), '-' + a); | |
} | |
} | |
// Store in the result array the value of the calculation, | |
// removing any carry. | |
result.push(digitsSum % 10); | |
// Store the carry derived from the sum performed. | |
carry = Math.floor(digitsSum / 10); | |
} | |
// If there is any carry left, add it to the final result. | |
if (carry !== 0) { | |
result.push(carry); | |
} | |
// The result is stored into an array in reverse order. | |
// So, the returned value must be reversed. Then, it | |
// must be turned into a string and any trailing | |
// (non-significant) zero must be removed. | |
// | |
// If the trim operation leaves an empty string, return zero. | |
return result.reverse() | |
.join('') | |
.replace(/^0+/, '') || '0'; | |
} | |
// If no arguments are provided, default the result to zero. | |
if (arguments.length === 0) { | |
return '0'; | |
} | |
// Calculate the sum of the arguments two at a time. | |
return [].reduce.call( | |
arguments, | |
function(previous, current) { | |
return sum(previous, current); | |
}, | |
'0' | |
); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Strictly compares two values. | |
* | |
* Returns <code>true</code> if the values are stricly equal; | |
* <code>false</code> otherwise. | |
* | |
* @param {*} actual The first value | |
* @param {*} expected The second value | |
* @param {string} [message] A message to display | |
* | |
* @return {boolean} | |
*/ | |
function strictEqual(actual, expected, message) { | |
if (actual === expected) { | |
console.log((message ? message : actual + ' is equal to ' + expected) + ': OK!'); | |
} else { | |
console.warn((message ? message + ': ' : '') + 'Expected ' + actual + ' to be ' + expected); | |
} | |
return actual === expected; | |
} | |
function testSumStrings() { | |
strictEqual(sumStrings(), '0', 'No arguments'); | |
strictEqual(sumStrings('5'), '5', 'One argument'); | |
strictEqual(sumStrings('16', '29'), '45', 'Two arguments with both positive'); | |
strictEqual(sumStrings('-22', '19'), '-3', 'Two arguments with the first negative and greater than the positive'); | |
strictEqual(sumStrings('-12', '19'), '7', 'Two arguments with the first negative and less than the positive'); | |
strictEqual(sumStrings('19', '-22'), '-3', 'Two arguments with the second negative and greater than the positive'); | |
strictEqual(sumStrings('29', '-12'), '17', 'Two arguments with the second negative and less than the positive'); | |
strictEqual(sumStrings('-16', '-29'), '-45', 'Two arguments with both negative'); | |
strictEqual(sumStrings('-122', '19'), '-103', 'Two arguments with digits to carry'); | |
strictEqual(sumStrings('1', '2', '3', '4', '5'), '15', 'Multiple arguments'); | |
strictEqual(sumStrings('15', '-12', '24', '-7', '-9'), '11', 'Multiple arguments with a mix of positive and negative integers'); | |
strictEqual(sumStrings('10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', '10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'), '20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', 'Two arguments whose sum exceeds the maximum number'); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment