reptile7's JavaScript blog
Tuesday, December 31, 2019
 
Adding and Clearing Columns of Numbers
Blog Entry #405

We've got two more College Tuition Chooser functions to put under the microscope, they being computeForm( ) and formClear( ). Before we get started, however, let's review a bit...

Calling the computeForm( ) function

Like the validateData( ) function, the computeForm( ) function is called from the calc( ) function, which is redetailed below in its entirety.

function calc(input) {
    if (agentInfo == "Netscape 4.0") {
        input.value = validateData(input.value);
        computeForm(input.form); }
    else {
        if (validateData(input.value)) computeForm(input.form);
        else {
            window.alert("Please do not use any characters besides numbers on this line. Your entry will be erased.");
            input.value = 0; } } }


For Netscape 4.0x users, every calc(this) call ultimately effects a computeForm(input.form) call. There are no input.values for which this does not happen: if the user's a1 input is hello world, then the validateData( ) function's correctedNum number stays at 0, which is returned to the calc( ) function and then passed on to the computeForm( ) function. The validateData( ) function does discard arithmetically unacceptable input characters but in no cases is the user alert( )ed that a given input itself may be unacceptable.

For non-Netscape 4.0x users, a calc(this) call effects a computeForm(input.form) call only if the input.value contains only digit and decimal point characters or is blank. When the not-for-Netscape 4.0 part of the validateData( ) function runs into an unapproved ch character, the not-for-Netscape 4.0 part of the calc( ) function alert( )s the user accordingly and sets the input.value to 0, and no further processing occurs.

Taking everything into account, the not-for-Netscape 4.0 approach is in my book better than the for-Netscape 4.0 approach: a problematic input should be blocked (if not erased) before any addition gets under way.

Adding it all up

Suppose we want to add a series of eight numbers via an OpenOffice spreadsheet.
We type the numbers individually into the spreadsheet's A1...A8 cells and then give focus to the A9 cell.
We access the spreadsheet's Insert menu → Function... Wizard.
We select the SUM option from the wizard's FunctionsFunction menu.
We click the button at the bottom of the wizard to input an =SUM( ) pre-formula into the wizard's Formula box.
We plug an A1:A8 parameter range into the =SUM( ) pre-formula.
We click the wizard's button to post the completed =SUM(A1:A8) formula and resulting sum to the A9 cell.

The College Tuition Chooser doesn't wait for a full column of costs to get summing, however. Per the above calc( ) code, each post-validation input.value fires the computeForm( ) function, which discretely sums the cost values for the a# and b# and c# columns and outputs the totals in the a9 and b9 and c9 fields, respectively.

function computeForm(form) {
    form.a9.value = form.a1.value * 1 + form.a2.value * 1 + form.a3.value * 1 + form.a4.value * 1 + form.a5.value * 1 + form.a6.value * 1 + form.a7.value * 1 + form.a8.value * 1;
    form.b9.value = form.b1.value * 1 + form.b2.value * 1 + form.b3.value * 1 + form.b4.value * 1 + form.b5.value * 1 + form.b6.value * 1 + form.b7.value * 1 + form.b8.value * 1;
    form.c9.value = form.c1.value * 1 + form.c2.value * 1 + form.c3.value * 1 + form.c4.value * 1 + form.c5.value * 1 + form.c6.value * 1 + form.c7.value * 1 + form.c8.value * 1; }


The various values are numberized for arithmetic addition via * 1 multiplications (otherwise they'd be concatenated); importantly, blanks are mapped to 0s for those multiplications by almost all browsers. A 10000 a1 input therefore gives a 10000 a9 output but it also gives a 0 b9 output and a 0 c9 output.

Total Cost of Attendance$$$

The calc( )/computeForm( ) functionality updates the a9 and b9 and c9 values automatically as the user fills in the chooser form.

The computeForm( ) statements clash with my organizational bent. Why are we writing out all those terms? I like to do things iteratively whenever possible, so here's how I would carry out the addition:

var columnID = new Array("a", "b", "c"); /* Netscape 3.x doesn't support array literals. */
for (var i = 0; i < columnID.length; i++) {
    var runningfieldtotal = 0;
    for (var j = 1; j < 9; j++)
        runningfieldtotal += Number(form.elements[columnID[i] + j].value); /* Number("") returns 0. */
    form.elements[columnID[i] + 9].value = runningfieldtotal; }


Moreover, an array-loop approach would make it much easier to scale the addition if we were to add more columns and rows to the chooser.

Netscape 2.x and the computeForm( ) function

Navigator 2.0 does not map blanks to 0s in the computeForm( ) function but instead throws a value is not a numeric literal error as soon as it hits a "" * 1 multiplication - at least that's what I see on my iMac. I'm sure that some people were still using Netscape 2.x when the chooser went live, and those users could have been accommodated by the preceding for-based addition code upon making a few simple modifications thereto:
(1) Use an Object object to array the a and b and c column identifiers.
(2) Explicitly convert blanks to 0s before attempting to add them.
(3) Numberize the cost values with the eval( ) function.

var columnID = new Object( ); /* Netscape 2.x doesn't support the Array object at all. */
columnID[0] = "a";
columnID[1] = "b";
columnID[2] = "c";
columnID.length = 3;
for (var i = 0; i < columnID.length; i++) {
    var runningfieldtotal = 0;
    for (var j = 1; j < 9; j++) {
        if (! form.elements[columnID[i] + j].value.length) form.elements[columnID[i] + j].value = 0;
        runningfieldtotal += eval(form.elements[columnID[i] + j].value); } /* eval("") returns undefined. */
    form.elements[columnID[i] + 9].value = runningfieldtotal; }


Actually, there's no reason why we couldn't use this code with modern browsers; re the danger of using eval( ), malicious code inputted by a malicious user would be blocked at the validation stage.

The form.elements[columnID[i] + j].value = 0 assignments load 0s into the rows[1]-rows[8] non-input cost fields; if you don't like that (I don't care for it myself), then you can blank those fields with:

var form2 = null;
function computeForm(form) {
    ...addition code...
    form2 = form;
    window.setTimeout("for (var i = 3; i < 27; i++) if (form2.elements[i].value == '0') form2.elements[i].value = '';", 10); }


One column at a time

If you would prefer to add up just one column of costs at a time à la a standard spreadsheet, then that's pretty easy to arrange:

function computeForm(input) {
    var columnID = input.name.charAt(0);
    var runningfieldtotal = 0;
    for (var i = 1; i < 9; i++)
        runningfieldtotal += Number(input.form.elements[columnID + i].value);
    input.form.elements[columnID + 9].value = runningfieldtotal; }

/* In the calc( ) function, if the input.value passes a regexp validation or is blank: */
if (cost_pattern.test(input.value) || ! input.value.length)
    computeForm(input);


I trust you are up to the task of modifying this computeForm( ) version for all of the Netscape 2.x users out there. ;-)

Back to the beginning

The chooser's formClear( ) function empties all of the cost fields, including the Total Cost of Attendance fields, and is called by clicking the button at the bottom of the display.

function formClear(form) {
    form.a1.value = ""; form.a2.value = ""; form.a3.value = ""; form.a4.value = "";
    form.a5.value = ""; form.a6.value = ""; form.a7.value = ""; form.a8.value = "";
    form.a9.value = ""; form.b1.value = ""; form.b2.value = ""; form.b3.value = "";
    form.b4.value = ""; form.b5.value = ""; form.b6.value = ""; form.b7.value = "";
    form.b8.value = ""; form.b9.value = ""; form.c1.value = ""; form.c2.value = "";
    form.c3.value = ""; form.c4.value = ""; form.c5.value = ""; form.c6.value = "";
    form.c7.value = ""; form.c8.value = ""; form.c9.value = ""; }


Again, there is no need to process the fields individually when we can do so iteratively, in this case with just one line of code:

for (var i = 3; i < 30; i++) form.elements[i].value = "";

To clear the Name of Institution fields as well, we can supplement the button with a traditional reset button:

<tr><td id="resetTd" colspan="4">
<input type="button" value="Just Clear the Numbers" onclick="formClear(this.form);">
<input type="reset" value="Clear All Fields">
</td></tr>

We're almost ready to wrap up our College Tuition Chooser discourse. I would still like to add an =AVERAGE(number1, number2, ...) capability to the chooser and then provide a demo along with some summary commentary, and I'll do so in the following entry; depending on how long that takes, I may also discuss an alternative, JavaScript-based approach to the creation of a spreadsheet template that I've been working on.

Comments: Post a Comment

<< Home

Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)