Server-Side Scripting/Associative Arrays/Go

routes/lesson7.go

edit
// This program reads a user-selected text file of countries
// and Celsius temperatures. It displays the data in Celsius
// and Fahrenheit sorted in descending order by temperature.
//
// File format:
// Country,MaximumTemperature
// Bulgaria,45.2 °C
// Canada,45 °C
//
// References:
//  https://www.mathsisfun.com/temperature-conversion.html
//  https://golang.org/doc/
//  https://tutorialedge.net/golang/go-file-upload-tutorial/
//  https://gobyexample.com/structs

package routes

import (
    "html/template"
    "io/ioutil"
    "net/http"
    "mime/multipart"
    "path/filepath"
    "sort"
	"strconv"
    "strings"
)

type Temperature7 struct {
    country     string
    celsius     float64
    fahrenheit  float64
}

func Lesson7(response http.ResponseWriter, request *http.Request) {
	response.Header().Set("Content-Type", "text/html; charset=utf-8")

	type Data struct {
		Table  template.HTML
	}
	
	result := ""

    switch request.Method {
        case "GET":
            result = ""
        case "POST":
            file, handler, err := request.FormFile("file")
            result = "<h2>" + handler.Filename + "</h2>"
            if err != nil {
                result += err.Error()
            } else {
                result += processFile7(file);
            }
        default:
            result = "Unexpected request method: " + request.Method
    }

	data := Data{template.HTML(result)}
	path := filepath.Join("templates", "lesson7.html")
	parsed, _ := template.ParseFiles(path)
	parsed.Execute(response, data)
}

func processFile7(file multipart.File) string {
    bytes, err := ioutil.ReadAll(file)
    if (err != nil) {
        return err.Error()
    }

    text := string(bytes)
    text = strings.TrimSpace(text)
    lines := strings.Split(text, "\n")
    if strings.Index(lines[0], "Country,MaximumTemperature") == 0 {
        // remove heading line
        lines = lines[1:]
    }

    var table []Temperature7
    for _, line := range lines {
        temperature := processLine7(line)
        table = append(table, temperature)
    }

    sort.Slice(table, func(a, b int) bool {
        return table[a].celsius > table[b].celsius
    })

    result := formatTable7(table)
    return result
}

func processLine7(line string) Temperature7 {
    slice := strings.Split(line, ",")
    if len(slice) != 2 {
        return Temperature7{"Invalid file format: ", 0, 0}
    }

    index := strings.Index(slice[1], " °C")
    if index < 0 {
        return Temperature7{"Invalid file format: ", 0, 0}
    }

    celsius, err := strconv.ParseFloat(slice[1][0:index], 64)
    if err != nil {
        return Temperature7{err.Error(), 0, 0}
    }

    fahrenheit := celsius * 9 / 5 + 32
    temperature := Temperature7{
        country: slice[0],
        celsius: celsius,
        fahrenheit: fahrenheit}
    return temperature
}

func formatTable7(table []Temperature7) string {
    result := "<table><tr><th>Country</th>"
    result += "<th>Celsius</th><th>Fahrenheit</th></tr>"

    for _, temperature := range table {
        result += "<tr><td>" + temperature.country + "</td>"
        result += "<td>" + strconv.FormatFloat(temperature.celsius, 'f', 1, 64) + "° C</td>"
        result += "<td>" + strconv.FormatFloat(temperature.fahrenheit, 'f', 1, 64) + "° F</td></tr>"
    }

    result += "</table>"
    return result
}

templates/lesson7.html

edit
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Lesson 7</title>
    <link rel="stylesheet" href="styles.css">
    <style>
        p {
            min-height: 1em;
        }
    </style>
</head>

<body>
    <h1>Temperature Conversion</h1>
    <p>Select a file of Celsius temperatures for conversion:</p>
    <form method="POST" enctype="multipart/form-data">
        <input type="file" id="file" name="file">
        <input type="submit">
    </form>
    {{.Table}}
</body>

</html>

Try It

edit

See Server-Side Scripting/Routes and Templates/Go to create a test environment.