Part 013 Go Template Comparison Functions

Having an amazing eq and an equally amazing gt I just had to extend this int a set of comparison functions. I have added ge, lt, and le.

The output shows each of them working and how they work.

An example (ex13.go):

package main

// (C) Philip Schlump, 2013.
// MIT Licensed, see LICENSE.txt

// A set of comparison functions.

import (
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"os"
	"reflect"
)

// -----------------------------------------------------------------------------
//
// Function: "eq"
// I didn't write "eq" - I think that it is amazing!
//
// -----------------------------------------------------------------------------
//
// From:  https://groups.google.com/forum/#!topic/golang-nuts/OEdSDgEC7js
// By:Russ Cox
//
//   Comment by:
//   Rob 'Commander' Pike
//   Date: 4/4/12
//
//   I would like to point out the loveliness of Russ's eq function. The
//   beauty is concentrated in the range loop, where x==y is an interface
//   equality check that verifies type and value equality while avoiding
//   allocation. It is enabled by the "case string, int, ..." etc. line,
//   which does the type check but leaves x of interface type.
//   By the way, there could be more types on that case; if you borrow this
//   function, be sure to add the types you need.
//
//   -rob
//
// I have found this function to be useful in my templates.
// I install it as "eq".
//
// eq reports whether the first argument is equal to one of the
// succeeding arguments.
//
func eq(args ...interface{}) bool {
	if len(args) == 0 {
		return false
	}
	x := args[0]
	switch x := x.(type) {
	case string, int, int64, byte, float32, float64:
		for _, y := range args[1:] {
			if x == y {
				return true
			}
		}
		return false
	}

	for _, y := range args[1:] {
		if reflect.DeepEqual(x, y) {
			return true
		}
	}
	return false
}

// -----------------------------------------------------------------------------
//
// Function: "gt"
// I didn't write "gt" - This is from the same thread as "eq".
//
// -----------------------------------------------------------------------------
//
// From:  https://groups.google.com/forum/#!topic/golang-nuts/OEdSDgEC7js
//
// gt returns true if the arguments are of the same type (with int8 and int64
// as the same type) and the first argument is greater than the second. This
// is only defined on string, intX, uintX and floatX all other types return
// false.
//
func gt(a1, a2 interface{}) bool {
	switch a1.(type) {
	case string:
		switch a2.(type) {
		case string:
			return reflect.ValueOf(a1).String() > reflect.ValueOf(a2).String()
		}
	case int, int8, int16, int32, int64:
		switch a2.(type) {
		case int, int8, int16, int32, int64:
			return reflect.ValueOf(a1).Int() > reflect.ValueOf(a2).Int()
		}
	case uint, uint8, uint16, uint32, uint64:
		switch a2.(type) {
		case uint, uint8, uint16, uint32, uint64:
			return reflect.ValueOf(a1).Uint() > reflect.ValueOf(a2).Uint()
		}
	case float32, float64:
		switch a2.(type) {
		case float32, float64:
			return reflect.ValueOf(a1).Float() > reflect.ValueOf(a2).Float()
		}
	}
	return false
}

func ge(a1, a2 interface{}) bool {
	switch a1.(type) {
	case string:
		switch a2.(type) {
		case string:
			return reflect.ValueOf(a1).String() >= reflect.ValueOf(a2).String()
		}
	case int, int8, int16, int32, int64:
		switch a2.(type) {
		case int, int8, int16, int32, int64:
			return reflect.ValueOf(a1).Int() >= reflect.ValueOf(a2).Int()
		}
	case uint, uint8, uint16, uint32, uint64:
		switch a2.(type) {
		case uint, uint8, uint16, uint32, uint64:
			return reflect.ValueOf(a1).Uint() >= reflect.ValueOf(a2).Uint()
		}
	case float32, float64:
		switch a2.(type) {
		case float32, float64:
			return reflect.ValueOf(a1).Float() >= reflect.ValueOf(a2).Float()
		}
	}
	return false
}

func lt(a1, a2 interface{}) bool {
	switch a1.(type) {
	case string:
		switch a2.(type) {
		case string:
			return reflect.ValueOf(a1).String() < reflect.ValueOf(a2).String()
		}
	case int, int8, int16, int32, int64:
		switch a2.(type) {
		case int, int8, int16, int32, int64:
			return reflect.ValueOf(a1).Int() < reflect.ValueOf(a2).Int()
		}
	case uint, uint8, uint16, uint32, uint64:
		switch a2.(type) {
		case uint, uint8, uint16, uint32, uint64:
			return reflect.ValueOf(a1).Uint() < reflect.ValueOf(a2).Uint()
		}
	case float32, float64:
		switch a2.(type) {
		case float32, float64:
			return reflect.ValueOf(a1).Float() < reflect.ValueOf(a2).Float()
		}
	}
	return false
}

func le(a1, a2 interface{}) bool {
	switch a1.(type) {
	case string:
		switch a2.(type) {
		case string:
			return reflect.ValueOf(a1).String() <= reflect.ValueOf(a2).String()
		}
	case int, int8, int16, int32, int64:
		switch a2.(type) {
		case int, int8, int16, int32, int64:
			return reflect.ValueOf(a1).Int() <= reflect.ValueOf(a2).Int()
		}
	case uint, uint8, uint16, uint32, uint64:
		switch a2.(type) {
		case uint, uint8, uint16, uint32, uint64:
			return reflect.ValueOf(a1).Uint() <= reflect.ValueOf(a2).Uint()
		}
	case float32, float64:
		switch a2.(type) {
		case float32, float64:
			return reflect.ValueOf(a1).Float() <= reflect.ValueOf(a2).Float()
		}
	}
	return false
}

func readInFile(path string) string {
	file, err := ioutil.ReadFile(path)
	if err != nil {
		return ""
	}
	return string(file)
}

func readInJson(path string) map[string]interface{} {
	var jsonData map[string]interface{}
	file, err := ioutil.ReadFile(path)
	if err != nil {
		return jsonData
	}
	json.Unmarshal(file, &jsonData)
	return jsonData
}

func main() {

	if len(os.Args) != 3 {
		fmt.Printf("Usage: ex13 TemplateFileName Data.JSON\n")
		os.Exit(1)
	}

	var tmpl_fn string = os.Args[1] // Look for *literal* in the template
	var data_fn string = os.Args[2]

	tmpl := readInFile(tmpl_fn)
	person := readInJson(data_fn)

	funcMap := template.FuncMap{
		"eq": eq,
		"gt": gt,
		"ge": ge,
		"lt": lt,
		"le": le,
	}

	t := template.New("file-template").Funcs(funcMap)
	t, err := t.Parse(tmpl)
	if err != nil {
		panic(err)
		os.Exit(1)
	}

	err = t.ExecuteTemplate(os.Stdout, "file-template", person)
	if err != nil {
		panic(err)
		os.Exit(1)
	}
}


In this example the function bob is called from inside the template.

To run this you can

$ go run ex13.go ex13.tmpl ex13.json


To compile it and run it

$ go build ex13.go
$ ./ex13 ex13.tmpl ex13.json

With the input of (ex13.tmpl):


{{gt "a" "b"}} -- Should be false

{{gt 12 22}} -- Should be false

{{gt "c" "b"}} -- Should be true

{{gt 42 22}} -- Should be true

{{gt "z" "z"}} -- Should be false

{{gt 42 42}} -- Should be false

{{gt .Data1 .Data2}} -- {{.Data1}} is gt than {{.Data2}} is false

{{gt .Data2 .Data1}} -- {{.Data2}} is gt than {{.Data1}} is true

Other Comparision Funcitons

{{ge "a" "b"}} -- Should be false
{{ge "b" "b"}} -- Should be true
{{ge "c" "b"}} -- Should be true

{{lt "a" "b"}} -- Should be true
{{lt "b" "b"}} -- Should be false
{{lt "c" "b"}} -- Should be false

{{le "a" "b"}} -- Should be true
{{le "b" "b"}} -- Should be true
{{le "c" "b"}} -- Should be false



The output you should expect from running it is (ex13.out):


false -- Should be false

false -- Should be false

true -- Should be true

true -- Should be true

false -- Should be false

false -- Should be false

false -- [DATA 1] is gt than [DATA 2] is false

true -- [DATA 2] is gt than [DATA 1] is true

Other Comparision Funcitons

false -- Should be false
true -- Should be true
true -- Should be true

true -- Should be true
false -- Should be false
false -- Should be false

true -- Should be true
true -- Should be true
false -- Should be false



Code tested on: Ubuntu 13.04, Mac 13.8, Windows 7 in go 1.1.2


•       •       •       •       •       •

Summary: # of Words: 180
Author: Philip J. Schlump
Published On: 2013-10-23

Download code from this articles in .tar.gz for Mac/Linux/Unix or .zip with CR/LF for Windows format.

 

Before You Go....

Have you read "Unintend Consinsequences"?

"I laughed so hard it hurt..."
    Rod Brown

"Incredibly funny! Incredibly true!"
    Tad Stevens