JavaScript
Since JavaScript is being reported as one of the current
popular languages, I thought it time to try the Learn to
Code GR application that I previously wrote in Ada, C#, Python, Java, C#
again, C, Kotlin, and Pascal using JavaScript.
Getting started I had to use the internet once again as my
classroom. Took most a day to get
started since the advice I found didn't work.
Finally I found someone who knew what they were talking about so was
able to do a simple demo of html invoking an external function. Then, a similar simple demo of html invoking
an internal function. The working
samples are:
External function – script.index.js
function go(){
document.write("javascript is working");
}
– script.html
<html>
<head>
<script type="text/javascript"
src="script.index.js"></script>
</head>
<body>
<input
type="button" onclick="go()" value="Display
JS"/>
</body>
</html>
which displayed a browser page with a button with a label of
Display JS. When clicked javascript
is working was displayed.
Internal function – script-internal.html
<html>
<head>
<script>
function go(){
document.write("internal javascript is working");
}
</script>
</head>
<body>
<input
type="button" onclick="go()" value="Display Internal"/>
</body>
</html>
with similar results when script-internal.html was run.
Since reading the max-col-sum-by-key.tsv
text file is a necessary first step in reworking the Learn to Code GR
application I next tried various html files in an attempt to open and read the
file. I found various results with
internet searches but was unable to get them to work until I found one via a
Bing search for "javascript open named file" that provided an option
from stackoverflow of htm15 - How to read text file in JavaScript -
Stack Overflow.
This solution worked out-of-the-box where the html is
<!DOCTYPE html>
<html>
<head>
<title>Read File (via User Input selection)</title>
<script type="text/javascript">
var
reader; //GLOBAL File Reader object for demo purpose only
/**
* Check
for the various File API support.
*/
function
checkFileAPI() {
if
(window.File && window.FileReader && window.FileList &&
window.Blob) {
reader = new FileReader();
return true;
}
else {
alert('The File APIs are not fully supported by your browser. Fallback
required.');
return false;
}
}
/**
* read
text input
*/
function
readText(filePath) {
var
output = ""; //placeholder for text output
if(filePath.files && filePath.files[0]) {
reader.onload = function (e) {
output = e.target.result;
displayContents(output);
};//end onload()
reader.readAsText(filePath.files[0]);
}//end if html5 filelist support
else
if(ActiveXObject && filePath) { //fallback to IE 6-8 support via
ActiveX
try {
reader = new ActiveXObject("Scripting.FileSystemObject");
var file = reader.OpenTextFile(filePath, 1); //ActiveX File Object
output = file.ReadAll(); //text contents of file
file.Close(); //close file "input stream"
displayContents(output);
} catch (e) {
if (e.number == -2146827859) {
alert('Unable to access local files due to browser security settings. '
+
'To overcome this, go to Tools->Internet
Options->Security->Custom Level. ' +
'Find the setting for "Initialize and script ActiveX controls not
marked as safe" and change it to "Enable" or
"Prompt"');
}
}
}
else
{ //this is where you could fallback to Java Applet, Flash or similar
return false;
}
return true;
}
/**
*
display content using a basic HTML replacement
*/
function
displayContents(txt) {
var
el = document.getElementById('main');
el.innerHTML = txt; //display output in DOM
}
</script>
</head>
<body onload="checkFileAPI();">
<div
id="container">
<input type="file" onchange='readText(this)' />
<br/>
<hr/>
<h3>Contents of the Text file:</h3>
<div id="main">
...
</div>
</div>
</body>
</html>
This html opens a browser window with a Choose File button
with "No file chosen" to the right of it and then a dividing line
across the screen followed by
Contents of the
Text file:
below
the line. When the Choose File button
is clicked I was able to select the max-col-sum-by-key.tsv that I had
copied to the folder with the various html files. This resulted in all the records of the file being displayed in the
browser window as
0,912_NUM 1000 1 1 0,912_NUM 1000 2 1
0,912_NUM 1000 1 1 0,912_NUM 1000 2 2 0,912_NUM 1000 1 1 0,912_NUM 1000 2 2
0,912_NUM 1000 1 1 0,912_NUM 1000 1 1 0,912_NUM 1000 2 2 0,912_NUM 1000 1 1
0,912_NUM 2000 1 1 0,912_NUM 2000 1 1
etc.
I expect that this will be the final difficult problem
standing in the way of producing another version of the Learn to Code GR
problem. (This proved to be true. I started on 12/29 and finished the morning
of Jan 3 with bowl games and NFL reducing the available time.)
After opening and reading the tsv file I moved the parse
function to a .js file and did the functions that it calls in the .js file so I
could use node to catch some of the errors in advance. That is, I didn't see a way to do so for the
.html file. Even so, there were errors
that running node on the .js file didn't catch. For instance, references to an undefined variable.
Also, I reverted to how I kept track of the data in the
original C# solution since everything I found says that there isn't a class in
JavaScript. Although I modified the
update function from the original C# to use multiple functions as I had done in
the C solution.
I couldn't find anything to output to the browser window
from the .js file so I used the alert function for debugging.
Also, because I couldn't do output from the .js file, I
passed the results back to the .html file and displayed the results there.
This resulted in a browser window of
Before the file was chosen the display had the Choose File
button with the horizontal line and No
file chosen to the right of the button.
In both cases "Learn to Code GR" is the text shown on the
browser tab.
The two files to do the JavaScript version of the solution
are as follows.
LearntoCode.html
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript"
src="learntocode.js"></script>
<title>Learn to Code GR</title>
<script type="text/javascript">
var
reader; /*GLOBAL File Reader object*/
/*
*
Code to select, open, and read the text file.
*/
/*
Check for the various File API support. */
function checkFileAPI() {
if (window.File && window.FileReader && window.FileList
&& window.Blob) {
reader = new FileReader();
return true;
}
else {
alert('The File APIs are not fully supported by your browser. Fallback
required.');
return false;
}
} //
end checkFileAPI
/*
Read text input */
function readText(filePath) {
var buffer = ""; /* to contain the file text */
if(filePath.files && filePath.files[0]) {
reader.onload = function (e) {
buffer = e.target.result;
treatFile(buffer);
}; // end reader/onload function
reader.readAsText(filePath.files[0]);
}
// end if html5 filelist support
else if(ActiveXObject && filePath) { // fallback to IE 6-8
support via ActiveX
try {
reader = new ActiveXObject("Scripting.FileSystemObject");
var file = reader.OpenTextFile(filePath, 1); // ActiveX File Object
buffer = file.ReadAll(); // text contents of file
file.Close(); // close file "input stream"
treatFile(buffer);
} catch (e) {
if (e.number == -2146827859) {
alert('Unable to access local files due to
browser security ' +
'settings. ' + 'To overcome this, go
to ' +
'Tools->Internet
Options->Security->Custom Level. ' +
'Find the setting for
"Initialize and script ActiveX ' +
'controls not marked as safe"
and change it ' +
'to "Enable" or
"Prompt"');
}
}
}
else { // this is where you could fallback to Java Applet, Flash or
similar
return false;
}
return true;
} //
end readText
function display(results) {
var myDiv = document.getElementById("main");
var message =
"<br><b><u>Results</u></b>";
message += "<ul><li><b>KEY: </b>" +
results[0];
message += "<li><b>VALUE: </b>" +
results[1];
message += "<li><b>SUM: </b>" + results[2];
myDiv.innerHTML
= message;
} //
end display
function treatFile(buffer)
{
var results = [];
results = parse(buffer);
display(results);
} //
end treatFile
</script>
</head>
<body
onload="checkFileAPI();">
<input type="file" onchange='readText(this)' />
<hr/>
<div id="main">
</div>
</body>
</html>
learntocode.js
// Global data
// The following would be the KeyData and KeyTable
structures if there were
// something like struct or class in JavaScript.
var keyCount = 0;
var valueKey = [];
var valueCount = [];
var valueValues = [,]; // different values of the
key
var valueSums = [,]; // sum of values of particular key
// Data to be retained as the key and its associated
value with the maximum
// number of references.
var maxKey = 0;
var maxValue = 0;
var maxSum = 0;
// Saved data as parse each line.
var savedData = [];
savedData[0] = 0;
savedData[1] = 0;
savedData[2] = 0;
// Functions
// Parse data lines of the text file.
function parse(data) {
var CR =
"\r";
var HT =
"\t";
var LF =
"\n";
/* Parse
each line of data in the buffer to obtain the three fields of
*
interest, converting those fields to integers into an array, and
* then
updating a data structure to retain the data for evaluation
* when
the complete buffer has been parsed.
*/
var
nextField = 0; // index of the
beginning of next field
var
startField; // range of indexes of
var
numFields = 0; // index into
dataFields array
var
dataFields = []; // Integer values of the three fields of interest
dataFields[0] = 0;
dataFields[1] = 0;
dataFields[2] = 0;
var
bufSize = data.length;
var index
= 0;
for (index = 0; index < bufSize; index++
)
{
//
Parse the buffer line
if
(data[index] == HT) // beginning of a field
{ startField = nextField; //
save starting index
nextField = index + 1; // the
next byte will contain part of next field
if (numFields > 0)
{
//convert and store in dataFields
dataFields[numFields-1] = toInt(data, startField, index - 1);
}
// end if numFields > 0
numFields++;
} //
end if HT
// Do
the update when find CR or LF of reach the end of the buffer.
//
The last option since the file has no terminators for the last line.
if
((data[index] == CR) || (data[index] == LF) || (index == (bufSize-1)))
{
if (numFields < 4) // only do the update once for each line
{
//convert and store in dataFields
dataFields[numFields-1] = toInt(data, nextField, index - 1);
// Save the data to determine the key, value combination with
// maximum number of references
update(dataFields);
// Finished with the line in the data array, initialize
// for next line.
nextField = 0;
startField;
numFields = 0;
dataFields[0] = 0;
dataFields[1] = 0;
dataFields[2] = 0;
if ((data[index] == CR) && (data[index+1] == LF))
{ index++ } // bypass the extra char at end of line
}
// end if
} //
end if
} // end
for loop
// Report
the number of references with a particular Key and Value.
var
results = [];
results =
report();
return
results;
}; // end parse
// Convert character array to integer
function toInt(data, iS, iE)
{
var NINE
= "9" //57 // ASCII character for digit 9
var ZERO
= "0" //48 // ASCII character for digit 0
var index
= iE; // loop in reverse
var digit
= 0;
var m = 1; // multiplier for shift
var
number = 0; // Numeric result
while
(index >= iS)
{
if
((data[index] >= ZERO) && (data[index] <= NINE))
{
digit = data[index] - ZERO; // convert ASCII to digit
number = number + (m * digit);
m
= m * 10;
index--;
}
}
return
number;
} // end toInt
// Update the tables for a particular file line.
function update(data)
{
// This
function first checks if the key is new and, if so, adds it to the
// keys
array. It then does similar for the
values associated with it.
// Note:
data[0] is the key while data[1] and data[2] are the two values
// associated with the key.
// Save
data to be updated for use by updateTotals since needed by
//
multiple functions
savedData[0] = data[0];
savedData[1] = data[1];
savedData[2] = data[2];
// Check
whether the key is already in the key table
var
keyIndex = -1;
for (var
k = 0; k < keyCount; k++)
{
if
(valueKey[k] == data[0])
{
keyIndex = k; // key already in the table
break; // exit loop
}
} // end
for
if
(keyIndex < 0) // key not in the table
{ // add
the key
keyIndex = keyCount;
valueKey[keyIndex] = data[0];
valueCount[keyIndex] = 0;
keyCount++;
} // end
if
// Add
the values for the key
addValues(keyIndex, savedData[1], savedData[2]);
} // end update
// Add the values or increment their sums
function addValues(keyIndex, value1, value2)
{
// Check
whether the first value is already in the table
var
valueIndex = -1;
for (var
v = 0; v < valueCount[keyIndex]; v++)
{
if
(valueValues[keyIndex,v] == value1)
{ //
value already in the table
valueIndex = v;
valueSums[keyIndex,v]++; // increment its number of references
if (value1 == value2) // 2nd value the same
{
valueSums[keyIndex,v]++; // increment again
}
// check if new max
updateTotals(savedData[0],value1,valueSums[keyIndex,v]);
break; // exit loop
}
} // end
loop
if
(valueIndex < 0) // value not yet in table
{ // add
value to the table - index points to last value checked
var
index = valueCount[keyIndex];
valueValues[keyIndex,index] = value1;
valueSums[keyIndex,index] = 1;
valueCount[keyIndex]++;
if
(value1 == value2) // dupicated value
{
valueSums[keyIndex,index]++; // increment to 2
// check if new max
updateTotals(savedData[0],value1,valueSums[keyIndex,index]);
}
else
{
add2ndValue(keyIndex, value2);
}
} // end outer if
} // end addValues
function add2ndValue(keyIndex, value2)
{
// Check
whether the second value is already in the table
var
valueIndex = -1;
for (var
v = 0; v < valueCount[keyIndex]; v++)
{
if
(valueValues[keyIndex,v] == value2)
{ //
value already in the table
valueIndex = v;
valueSums[keyIndex,valueIndex]++; // increment number of references
// check if new max
updateTotals(savedData[0],value2,valueSums[keyIndex,valueIndex]);
break; // exit loop
}
} // end
loop
if
(valueIndex < 0) // value not yet in table
{ // add
value to the table
var
index = valueCount[keyIndex];
valueValues[keyIndex,index] = value2;
valueSums[keyIndex,index]
= 1;
valueCount[keyIndex]++;
updateTotals(savedData[0],value2,valueSums[keyIndex,index]);
}
} // end add2ndValue
function updateTotals(key, value, sum)
{
if (sum
> maxSum)
{
maxKey = key;
maxValue = value;
maxSum = sum;
}
} // end updateTotals
// Report the key and value with the most references
function report()
{
var
results = [];
results[0] = maxKey;
results[1] = maxValue;
results[2] = maxSum;
return
results;
} // end report
No comments:
Post a Comment