Applied Programming/Dictionaries and Sets/JavaScript

From Wikiversity
Jump to navigation Jump to search

objects.js[edit | edit source]

/* """This program demonstrates file, list, and tuple processing.

It creates a temporary file, adds data to the file, and reads the file
into an array of objects. It then provides a menu of options for displaying 
and searching the file data.

It will not run if the file already exists.

Input:
    None

Output:
    A temporary file and file contents.

References:

*/

const fs = require("fs");

if (typeof module != "undefined" && !module.parent) {
    main();
}

/**
  * Runs the main program logic.
  */
function main()
{
    let filename = "~temperatures.txt";

    if(fileExists(filename))
    {
        console.log(`File already exists: ${filename}`)
        process.exit(1)
    }

    createFile(filename);
    let temperatures = readFile(filename);
    deleteFile(filename);

    while (true) {
        let choice = getChoice();
        switch (choice) {
            case "1":
                displayTemperatures(temperatures, "Celsius")
                break;
            case "2":
                displayTemperatures(temperatures, "Fahrenheit")
                break;
            case "3":
                searchTemperatures(temperatures, "Celsius")
                break;
            case "4":
                searchTemperatures(temperatures, "Fahrenheit")
                break
            default:
                return;
        }    
    }
}

/**
 * Creates a text file.
 * 
 * @param Text filename to create.
 */
function createFile(filename)
{
    let data = '"C","F"\n';
    for(let celsius = 0; celsius <= 100; celsius += 10)
    {
        let fahrenheit = celsius * 9 / 5 + 32;
        data += `${celsius.toFixed(1)},${fahrenheit.toFixed(1)}\n`;
    }

    try {
        fs.writeFileSync(filename, data);
    } catch (error) {
        console.error(error);
    }
}

/**
 * Reads a text file.
 * 
 * File must be formatted as CSV with:
 * "C","F"
 * number,number
 * 
 * @param Text filename to read
 * @returns array of arrays
 * @throws RangeError if file format is invalid
 */
function readFile(filename)
{
    try {
        let data = fs.readFileSync(filename, "utf-8");
        let lines = data.trim().split("\n");
        
        let header = lines[0];
        if (header != '"C","F"') {
            throw new RangeError(`Invalid header format found in ${filename}: ${header}`)
        }

        lines.shift();
        let temperatures = [];
        for(const line of lines) {
            let row = line.split(",");
            if (row.length != 2 || isNaN(row[0]) || isNaN(row[1])) {
                throw new RangeError(`Invalid data format found in ${filename}: ${row}`)
            }

            let record = {
                C: Number(row[0]),
                F: Number(row[1])
            };
            temperatures.push(record);
        }

        return temperatures;
    } catch (error) {
        console.error(error);
        process.exit(2)
    }
}

/**
 * Deletes a file.
 * 
 * @param Text filename to delete.
 */
function deleteFile(filename)
{
    try {
        fs.unlinkSync(filename);
    } catch (error) {
        console.error(error);
    }
}

/**
 * Checks to see if a file exists.
 * 
 * @param Text filename to check.
 * @returns true if exists or false if not.
 */
function fileExists(filename)
{
    return fs.existsSync(filename);
}

/**
 * Input function to get input in Node environment.
 * 
 * @param {string} text prompt
 * @returns {string} input
 */
 function prompt(text) {
    const rls = require('readline-sync');
    let value = rls.question(text);
    return value;
}

/**
 * Displays menu and gets user choice.
 * 
 * @returns text choice or empty string.
 */
function getChoice() {
    const menu = "Select from the following options or press <Enter> to quit:\n" +
        "1. Display table sorted by Celsius temperature.\n" +
        "2. Display table sorted by Fahrenheit temperature.\n" +
        "3. Search for Celsius temperaturen.\n" +
        "4. Search for Fahrenheit temperature.\n";

    while (true) {
        console.log(menu);
        let choice = prompt("Enter choice: ");
        console.log("");
        if (choice === "") {
            return choice;
        }

        if (choice.length == 1 && "1234".includes(choice)) {
            return choice;
        }

        console.log(`${choice} is not a valid choice.\n`);        
    }
}

/**
 * Displays temperature array.
 * 
 * @param array temperatures.
 * @param scale "Celsius" or "Fahrenheit"
 * @throws RangeError if scale is invalid
 */
function displayTemperatures(temperatures, scale) {
    let fields = undefined;

    switch (scale) {
        case "Celsius":
            temperatures.sort(compareCelsius);
            fields = ["C", "F"];
            break;
        case "Fahrenheit":
            temperatures.sort(compareFahrenheit);
            fields = ["F", "C"];
            break;
        default:
            throw new RangeError("Scale must be Celsius or Fahrenheit");
    }

    console.log(`${fields[0]}\t${fields[1]}`);
    for(const record of temperatures) {
        console.log(`${record[fields[0]]}\t${record[fields[1]]}`);
    }

    console.log("");
}

/**
 * Get temperature to search for.
 * 
 * @param scale "Celsius" or "Fahrenheit"
 * @throws RangeError if scale is invalid
 * @returns Number or empty string
 */
function getTemperature(scale) {
    if (!["Celsius", "Fahrenheit"].includes(scale)) {
        throw new RangeError("Scale must be Celsius or Fahrenheit");
    }

    let temperature = prompt(`Enter ${scale} temperature: `);
    if (temperature == "") {
        return temperature;
    }

    if (isNaN(temperature)) {
        return "";
    }

    return Number(temperature);
}

/**
 * Searches temperature array to display nearest values.
 * 
 * @param array temperatures
 * @param scale "Celsius" or "Fahrenheit"
 * @throws RangeError if scale is invalid
 */
function searchTemperatures(temperatures, scale) {
    let searchElement = undefined;
    let displayElement = undefined;
    let otherScale = undefined;

    switch (scale) {
        case "Celsius":
            temperatures.sort(compareCelsius);
            searchElement = "C";
            displayElement = "F";
            otherScale = "Fahrenheit";
            break;
        case "Fahrenheit":
            temperatures.sort(compareFahrenheit);
            searchElement = "F";
            displayElement = "C";
            otherScale = "Celsius";
            break;
        default:
            throw new RangeError("Scale must be Celsius or Fahrenheit");
    }

    let temperature = getTemperature(scale);
    if (temperature === "") {
        return;
    }

    let last = temperatures[0];
    let row = undefined;
    for (row of temperatures) {
        if (temperature < row[searchElement]) {
            break;
        }
        last = row;
    }

    if (temperature < last[searchElement]) {
        console.log(`${temperature.toFixed(1)} ${scale} ` + 
            `is less than ${last[displayElement].toFixed(1)} ${otherScale}\n`)
    } else if (temperature == last[searchElement]) {
        console.log(`${temperature.toFixed(1)} ${scale} ` + 
            `is ${last[displayElement].toFixed(1)} ${otherScale}\n`)
    } else if (temperature > row[searchElement]) {
        console.log(`${temperature.toFixed(1)} ${scale} ` + 
            `is greater than ${last[displayElement].toFixed(1)} ${otherScale}\n`)
    } else {
        console.log(`${temperature.toFixed(1)} ${scale} ` + 
            `is between ${last[displayElement].toFixed(1)} ${otherScale} ` +
            `and ${row[displayElement].toFixed(1)} ${otherScale}\n`)
    }
}

/**
 * Compare temperature array elements for sort by Celsius.
 * 
 * @param a temperature element
 * @param b temperature element
 * @returns a - b
 */
function compareCelsius(a, b) {
    return Number(a.C) - Number(b.C);
}

/**
 * Compare temperature array elements for sort by Fahrenheit.
 * 
 * @param a temperature element
 * @param b temperature element
 * @returns a - b
 */
 function compareFahrenheit(a, b) {
    return Number(a.F) - Number(b.F);
}

Try It[edit | edit source]

Copy and paste the code above into one of the following free online development environments or use your own JavaScript compiler / interpreter / IDE.

See Also[edit | edit source]