How to compare 2 maps and export the difference in Golang? - go

I have 2 yaml files that I've exported to 2 different map variables. My yaml files have this example of format:
file1.yaml
a: b
c: d
e:
- f
- g
x:
- y
file2.yaml
m: n
c: d
x:
- y
- z
I've unmarshalled the yaml files into structs. My goal is to now compare the 2 maps, and only output the difference found in file2.yaml. So far I have something simple:
type ParametersYaml struct {
Parameters Params `yaml:"parameters""`
}
type Params map[string]interface{}
var pParam ParametersYaml
var sParam ParametersYaml
func Extractor(primaryYaml, secondaryYaml string) error {
primaryOpenedFile, err := fileOpener(primaryYaml)
secondaryOpenedFile, err := fileOpener(secondaryYaml)
err = yamlParser(primaryOpenedFile, &pParam)
err = yamlParser(secondaryOpenedFile, &sParam)
if err != nil {
return err
}
compare(pParam, sParam)
return err
}
func fileOpener(file string) ([]byte, error) {
f, err := ioutil.ReadFile(file)
return f, err
}
func yamlParser(fileObject []byte, param *ParametersYaml) error {
err := yaml.Unmarshal(fileObject, &param)
return err
}
func compare(pMarshalled, sMarshalled ParametersYaml) {
for s, sv := range sMarshalled.Parameters {
for p, pv := range pMarshalled.Parameters {
if s == p && sv == pv {
break
} else if s == p && sv != pv {
fmt.Printf("%s: %s,\n", s, sv)
break
} else if _, ok := pMarshalled.Parameters[s]; !ok {
fmt.Printf("%s: %s,\n", s, sv)
break
}
}
}
}
But it can't compare values with lists since the type is of interface{}.

There exists https://github.com/sters/yaml-diff which does the same thing you're intending to achieve.

Related

Slice automatically be sorted?

While I want to create my own pipeline to practice with goroutines, there's something particularly weird.
I use the random perm function to generate some int numbers, randomly obviously, I write them to IO writer and then read them form IO reader, cuz its binary source so I print them and they are sorted!!
Here's the code:
func RandomSource(tally int) chan int {
out := make(chan int)
sli := rand.Perm(tally)
fmt.Println(sli)
go func() {
for num := range sli {
out <- num
}
close(out)
}()
return out
}
func ReaderSource(reader io.Reader) chan int {
out := make(chan int)
go func() {
buffer := make([]byte, 8)
for ; ; {
n, err := reader.Read(buffer)
if n > 0 {
v := int(binary.BigEndian.Uint64(buffer))
out <- v
}
if err != nil {
break
}
}
close(out)
}()
return out
}
func WriterSink(writer io.Writer, in chan int) {
for v := range in {
buffer := make([]byte, 8)
binary.BigEndian.PutUint64(
buffer, uint64(v))
writer.Write(buffer)
}
}
func main() {
fileName := "small.in"
file, err := os.Create(fileName)
if err != nil {
log.Fatal(err)
}
defer file.Close()
p := RandomSource(500)
WriterSink(file, p)
file, err = os.Open(fileName)
if err != nil {
log.Fatal(err)
}
defer file.Close()
p = ReaderSource(file)
for v := range p {
fmt.Println(v)
}
}
range returns an index as the first value for an array or slice, which always goes from 0 up to len - 1. Use for _, num := range sli { if you want to iterate over the values themselves rather than the set of indices.

Image.At returns nil

I'm having an issue. Here is my code:
package main
import (
"math/rand"
"image/draw"
"image/png"
"image/color"
"strconv"
"os"
"time"
"fmt"
)
func genSites(width, height int) ([][]int) {
rand.Seed(time.Now().Unix())
l, err := strconv.Atoi(os.Args[len(os.Args)-2])
if err != nil {
panic(err)
}
sites := make([][]int, l)
for i := range sites {
sites[i] = make([]int, 2)
sites[i][0] = rand.Intn(width)
sites[i][1] = rand.Intn(height)
}
return sites
}
func main() {
inputF, err := os.Open(os.Args[len(os.Args)-3])
if err != nil {
panic(err)
}
defer inputF.Close()
inputR, err := png.Decode(inputF)
if err != nil {
panic(err)
}
input := inputR.(draw.Image)
minx, miny := input.Bounds().Min.X, input.Bounds().Min.Y
maxx, maxy := input.Bounds().Max.X-1, input.Bounds().Max.Y-1
sites := genSites(maxx-minx, maxy-miny)
siteColors := make([][]color.Color, len(sites))
//todo: figure out something better than this
for i := range siteColors {
siteColors[i] = make([]color.Color, (maxx-minx)*(maxy-miny))
}
siteBelongs := make([][]int, maxx - minx)
for x := range siteBelongs {
siteBelongs[x] = make([]int, maxy - miny)
for y := range siteBelongs[x] {
dmin := (maxx-minx)*(maxx-minx) + (maxy-miny)*(maxy-miny)
var smin int
for i, s := range sites {
d := (s[0]-x)*(s[0]-x) + (s[1]-y)*(s[1]-y)
if d > dmin {
smin = i
dmin = d
}
}
siteBelongs[x][y] = smin
siteColors[smin] = append(siteColors[smin], input.At(x+minx, y+miny))
}
}
siteAvgColors := make([]color.Color, len(sites))
for i := range siteAvgColors {
var sR, sG, sB, sA int
for _, val := range siteColors[i] {
fmt.Println(val)
r, g, b, a := val.RGBA()
sR += int(r)
sG += int(g)
sB += int(b)
sA += int(a)
}
siteAvgColors[i] = color.RGBA{
uint8(sR/len(siteColors[i])),
uint8(sG/len(siteColors[i])),
uint8(sB/len(siteColors[i])),
uint8(sA/len(siteColors[i]))}
}
for x := range siteBelongs {
for y := range siteBelongs[x] {
input.Set(minx + x, miny + y, siteAvgColors[siteBelongs[x][y]])
}
}
output, err := os.Create(os.Args[len(os.Args)-1])
if err != nil {
panic(err)
}
defer output.Close()
err = png.Encode(output, input)
if err != nil {
panic(err)
}
}
The error is this:
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x4b46e1]
goroutine 1 [running]:
main.main()
/home/victor/programs/goprograms/src/v/imagerandvornoi/main.go:71 +0x7a1
Line 71 is the one that says r, g, b, a := val.RGBA(). That val is inserted at line 63, or siteColors[smin] = append(siteColors[smin], input.At(x+minx, y+miny)), which means that input.At is returning nil. Why? How can I fix this?
This has to do with the semantics of the make() builtin within Go. make is special in that it can take two or three arguments:
make(Type, length) or make(Type, length, capacity). For the former, the length and capacity are set to the same value. If you're going to be assigning values to the slice using append, you're going to want to use the latter form. This is because the two argument form (make(Type, 10)) builds a slice with 10 elements in it already with the zero value, when you use append() it becomes the 11th item.
You're hitting a nil derefernece here because you're using the two-argument version of make with append, and so the first item in the slice is nil.

reader.ReadLine() doesn't advance after a scanner.Scan() call

The code below reads its values from this file:
2 3\n
1.0 2.0 3.0\n
-1.0 -2.0 -3.0\n
And should print:
[ {1 2 3}, {-1 -2 -3} ]
But instead I get this:
[{2 [31 2 3]} {0 []}] strconv.ParseFloat: parsing "3.0-1.0": invalid syntax
It seems that the reader.ReadLine() stays at the same location. Is there a simpler way to scan lines, then values inside each line?
package main
import (
"bufio"
"bytes"
"fmt"
"os"
"strconv"
"strings"
)
type Example struct {
classLabel int
attributes []float64
}
func NewExample(classLabel int, attributes []float64) *Example {
return &Example{classLabel, attributes}
}
func readFile(path string) ([]Example, error) {
var (
result []Example
err error
file *os.File
part []byte
size int
attributeNum int
)
if file, err = os.Open(path); err != nil {
return result, err
}
defer file.Close()
reader := bufio.NewReader(file)
buffer := bytes.NewBuffer(make([]byte, 0))
if part, _, err = reader.ReadLine(); err != nil {
return result, err
}
buffer.Write(part)
newLine := buffer.String()
fmt.Println("newLine=" + newLine)
r := strings.NewReader(newLine)
scanner := bufio.NewScanner(r)
scanner.Split(bufio.ScanWords)
if scanner.Scan() {
size, err = strconv.Atoi(scanner.Text())
if err != nil {
return result, err
}
}
fmt.Println("size=" + strconv.Itoa(size))
if scanner.Scan() {
attributeNum, err = strconv.Atoi(scanner.Text())
if err != nil {
return result, err
}
}
fmt.Println("attributeNum=" + strconv.Itoa(attributeNum))
result = make([]Example, size)
var classLabel int
var attributes []float64
for k := 0; k < size; k++ {
if part, _, err = reader.ReadLine(); err != nil {
return result, err
}
buffer.Write(part)
newLine := buffer.String()
fmt.Println("newLine=" + newLine)
r := strings.NewReader(newLine)
scanner := bufio.NewScanner(r)
scanner.Split(bufio.ScanWords)
if scanner.Scan() {
classLabel, err = strconv.Atoi(scanner.Text())
if err != nil {
return result, err
}
}
fmt.Println("classLabel=" + strconv.Itoa(classLabel))
for i := 0; i < attributeNum; i++ {
var attribute float64
if scanner.Scan() {
attribute, err = strconv.ParseFloat(scanner.Text(), 64)
if err != nil {
return result, err
}
attributes = append(attributes, attribute)
fmt.Println("attribute=" + strconv.FormatFloat(attribute, 'f', -1, 64))
}
}
result[k] = *NewExample(classLabel, attributes)
}
return result, scanner.Err()
}
func main() {
example, err := readFile("test.txt")
fmt.Println(example, err)
}
When you do this inside the for loop:
buffer.Write(part)
newLine := buffer.String()
fmt.Println("newLine=" + newLine)
The next line gets appended to buffer.
That is,
before the loop begins, buffer contains 2 3,
and then after reading 1.0 2.0 3.0,
it gets appended to buffer,
so the content becomes 2 31.0 2.0 3.0,
which you store in newLine.
That's where things start to go sideways.
You probably want to clear the buffer before reading each new line:
buffer.Reset()
buffer.Write(part)
newLine := buffer.String()
fmt.Println("newLine=" + newLine)
But then you will have further problems still, here:
if scanner.Scan() {
classLabel, err = strconv.Atoi(scanner.Text())
if err != nil {
return result, err
}
}
Since the line contains 1.0 2.0 3.0, the strconf.Atoi is going to fail.
I don't understand the purpose of this snippet,
perhaps you can delete it (or comment out).
With the above fixed, you will still have one more problem, on this line:
attributes = append(attributes, attribute)
Since attributes is never reset, it keeps growing.
That is, after the first line, it will contain 1 2 3,
and after the second line it will contain 1 2 3 -1 -2 -3.
You could correct that by moving the declaration of attributes without the outer loop, like this:
var attributes []float64
for i := 0; i < attributeNum; i++ {
var attribute float64
if scanner.Scan() {
attribute, err = strconv.ParseFloat(scanner.Text(), 64)
if err != nil {
return result, err
}
attributes = append(attributes, attribute)
fmt.Println("attribute=" + strconv.FormatFloat(attribute, 'f', -1, 64))
}
}

Go: deep copy slices

I would like to read a slice of strings representing hexadecimal numbers, and decode them to a slice of byte slices ([]string --> [][]byte). This is my code so far:
func (self *algo_t) decode_args(args []string) ([][]byte, error) {
var data [][]byte
for i := uint32(0); i < self.num_args; i++ {
data = make([][]byte, self.num_args)
tmp, err := hex.DecodeString(args[i])
fmt.Printf("i = %d\ttmp = %x\n", i, tmp)
data[i] = make([]byte, len(tmp))
copy(data[i], tmp)
if err != nil {
fmt.Fprintf(os.Stderr, "Error decoding hex string %s: %s\n", args[i], err.Error())
return nil, err
}
}
fmt.Printf("line 69\tdata[0] = %x\tdata[1] = %x\tdata[2] = %x\n",data[0], data[1], data[2])
return data, nil
}
calling this code and passing args = []string{"010203","040506","070809"} yields the following output:
i = 0 tmp = 010203
i = 1 tmp = 040506
i = 3 tmp = 070809
line 69 data[0] = data[1] = data[2] = 070809
Presumably the function returns [][]byte{[]byte{}, []byte{}, []byte{0x07, 0x08, 0x09}}.
I understand that this is because of the pointer behavior of Go; what is the best practice for doing a deep copy of this kind?
For example,
package main
import (
"encoding/hex"
"fmt"
)
// Decode hex []string to [][]byte
func decode(s []string) ([][]byte, error) {
b := make([][]byte, len(s))
for i, ss := range s {
h, err := hex.DecodeString(ss)
if err != nil {
err = fmt.Errorf(
"Error decoding hex string %s: %s\n",
ss, err.Error(),
)
return nil, err
}
b[i] = h
}
return b, nil
}
func main() {
s := []string{"010203", "040506", "070809"}
fmt.Println(s)
b, err := decode(s)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(b)
}
s = []string{"ABCDEF", "012345", "09AF"}
fmt.Println(s)
b, err = decode(s)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(b)
}
s = []string{"01", "123XYZ"}
fmt.Println(s)
b, err = decode(s)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(b)
}
}
Output:
[010203 040506 070809]
[[1 2 3] [4 5 6] [7 8 9]]
[ABCDEF 012345 09AF]
[[171 205 239] [1 35 69] [9 175]]
[01 123XYZ]
Error decoding hex string 123XYZ: encoding/hex: invalid byte: U+0058 'X'
There is a package built specifically to handle deep copy: http://godoc.org/code.google.com/p/rog-go/exp/deepcopy
You can look at the source here: https://code.google.com/p/rog-go/source/browse/exp/deepcopy/deepcopy.go. It covers copying slices and pointers, so it should cover your case.

How to make fmt.Scanln() read into a slice of integers

I have a line containing 3 numbers that I want to read from stdin with fmt.Scanln() but this code won't work:
X := make([]int, 3)
fmt.Scanln(X...)
fmt.Printf("%v\n", X)
I get this error message:
cannot use X (type []int) as type []interface {} in function argument
I don't get it.
Idiomatic Go would be:
func read(n int) ([]int, error) {
in := make([]int, n)
for i := range in {
_, err := fmt.Scan(&in[i])
if err != nil {
return in[:i], err
}
}
return in, nil
}
interface{} means nothing. Please don't use it if you don't have to.
For example,
package main
import "fmt"
func intScanln(n int) ([]int, error) {
x := make([]int, n)
y := make([]interface{}, len(x))
for i := range x {
y[i] = &x[i]
}
n, err := fmt.Scanln(y...)
x = x[:n]
return x, err
}
func main() {
x, err := intScanln(3)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%v\n", x)
}
Input:
1 2 3
Output:
[1 2 3]
I think the the correct version should be
X := make([]int, 3)
fmt.Scanln(&X[0], &X[1], &X[2])
fmt.Printf("%v\n", X)
This error message occurs b/c there's no reasonable way to convert []int to []interface{}. Note, this is in reference to a slice. So the syntax your using is correct, but fmt.Scanln expects []interface{}. This has implications outside of pkg fmt.
The reason I've seen given for this is due to Go giving you control over memory layout so it currently has no reasonable way to do the slice conversion. This means you'll need to do the conversion manually before passing it to a function expecting the slice of a given type. For example:
package main
import (
"fmt"
)
func main() {
x := make([]int, 3)
y := make([]interface{}, 3)
y[0] = x[0]
y[1] = x[1]
y[2] = x[2]
fmt.Println(y...)
}
Or something a little more general:
x := make([]int, 3)
y := make([]interface{}, len(x))
for i, v := range x {
y[i] = v
}
fmt.Println(y...)
Regarding your specific issue, see the following:
x := make([]*int, 3)
for i := range x {
x[i] = new(int)
}
y := make([]interface{}, 3)
for i, v := range x {
y[i] = v
}
if _, err := fmt.Scanln(y...); err != nil {
fmt.Println("Scanln err: ", err)
}
for _, v := range y {
val := v.(*int)
fmt.Println(*val)
}
I saw in a comment you said the lines can have different lengths. In that case
you can implement your own fmt.Scanner:
package main
import (
"bytes"
"fmt"
)
type slice struct {
tok []int
}
func (s *slice) Scan(state fmt.ScanState, verb rune) error {
tok, err := state.Token(false, func(r rune) bool { return r != '\n' })
if err != nil { return err }
if _, _, err := state.ReadRune(); err != nil {
if len(tok) == 0 {
panic(err)
}
}
b := bytes.NewReader(tok)
for {
var d int
_, err := fmt.Fscan(b, &d)
if err != nil { break }
s.tok = append(s.tok, d)
}
return nil
}
func main() {
var s slice
fmt.Scan(&s)
fmt.Println(s.tok)
}
https://golang.org/pkg/fmt#Scanner

Resources