Is it possible to sort a slice of structs by time in Golang? - slice

I am trying to sort the structs Session in the slice Session by each Session's start time and hall_id. Here is the code:
Sessions := []Session{
Session{
name: "superman",
hall_id: 1,
startTime: time.Date(2022, time.August, 15, 17, 35, 0, 0, time.UTC),
endTime: time.Date(2022, time.August, 15, 18, 35, 0, 0, time.UTC),
},
Session{
name: "thor",
hall_id: 2,
startTime: time.Date(2022, time.August, 15, 16, 30, 0, 0, time.UTC),
endTime: time.Date(2022, time.August, 15, 17, 30, 0, 0, time.UTC),
},
Session{
name: "joker",
hall_id: 3,
startTime: time.Date(2022, time.August, 15, 19, 40, 0, 0, time.UTC),
endTime: time.Date(2022, time.August, 15, 20, 30, 0, 0, time.UTC),
},
Session{
name: "batman",
hall_id: 1,
startTime: time.Date(2022, time.August, 15, 17, 40, 0, 0, time.UTC),
endTime: time.Date(2022, time.August, 15, 18, 20, 0, 0, time.UTC),
},
}
The point is that I am using "time" package, and in order to create a date, you need to use Date() function, which requires multiple stuff like: year int, month, day int, hour int, minute int, etc.
I have tried the AndreKR's answer which is:
slice.Sort(planets[:], func(i, j int) bool {
return planets[i].Axis < planets[j].Axis
})
but it seems that it does not work with multiple "parameters" of a struct. I tried this:
sort.Slice(Sessions[:], func(i, j int) bool {
return Sessions[i].startTime.Year() < Sessions[j].startTime.Year(),
int(Sessions[i].startTime.Month()) < int(Sessions[j].startTime.Month()),
Sessions[i].startTime.Day() < Sessions[j].startTime.Day(),
Sessions[i].startTime.Hour() < Sessions[j].startTime.Hour(),
Sessions[i].startTime.Minute() < Sessions[j].startTime.Minute()
})
I am new to the Golang, so if I have made an obvious mistake I am sorry ;(

Alright, I have got the answer:
sort.Slice(Sessions[:], func(i, j int) bool {
return Sessions[i].startTime.Before(Sessions[j].startTime)
})
this code will solve this problem

Related

strange behaviour of goroutines in for loop [duplicate]

This question already has answers here:
Strange golang "append" behavior (overwriting values in slice)
(3 answers)
Closed 2 years ago.
I am switching from Python to GoLang. I was learning goroutines. I faced strange outputs when I used the goroutines in for loop.
package main
import "fmt"
func main() {
var test = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40}
for _, x := range test{
go func() {
fmt.Println(x)
}()
}
}
the output:
25
19
28
19
40
40
6
I was thinking it was capturing values from for loop but, when I saw it was increasing and decreasing. I got confused. Why is that happening?
The goroutines are accessing the x variable that is changing in parallel with the execution of the goroutines. This is a common problem encountered by I think even go programmer at least once. You can capture the current value of the variable like this:
for _, x := range test{
go func(x int) {
fmt.Println(x)
}(x)
}
Or like this:
for _, x := range test{
x2 := x
go func() {
fmt.Println(x2)
}()
}

Finding numbers above the mean

I could use some help finding all the numbers from a struct array that are above a calculated mean!
//MeanMedianMode struct
type MeanMedianMode struct {
numbers []float64
}
func main() {
// range of numbers
dataType := MeanMedianMode{
numbers: []float64{
84, 25, 88, 56, 10, 19, 11, 80,
45, 83, 22, 40, 22, 52, 61, 13, 73, 23, //Data to be used
90, 89, 6,
},
}
I've figured out how to pass my data easily and find the average as follows...
//CalcMean float64
func (mm *MeanMedianMode) CalcMean() float64 {
total := 0.0
for _, v := range mm.numbers {
total += v
}
return (total / float64(len(mm.numbers)))
//return math.Round(total / float64(len(mm.numbers))) //Should it need to be rounded
}
My biggest issue is replicating that process and using the values stored in the array within another function and iterating over them to find the values greater than (>) the found mean!
I appreciate the insights!
I don't know how you'd like to do it, but something like this I guess:
package main
import (
"fmt"
)
//MeanMedianMode struct
type MeanMedianMode struct {
numbers []float64
}
func main() {
m := &MeanMedianMode{
numbers: []float64{
84, 25, 88, 56, 10, 19, 11, 80,
45, 83, 22, 40, 22, 52, 61, 13, 73, 23,
90, 89, 6,
},
}
mean := m.CalcMean()
for _, n := range m.numbers {
if n > mean {
fmt.Printf("%.3f is greater than the mean\n", n)
}
}
}
//CalcMean float64
func (mm *MeanMedianMode) CalcMean() float64 {
total := 0.0
for _, v := range mm.numbers {
total += v
}
return (total / float64(len(mm.numbers)))
}

How to extract a .jar file

I like to extract a .jar file in Go. I tried different approaches with the build in libraries but with no success. In buff is a little part of the JAR I try to analyze. The errors also occur on the full byte array.
flate:
buff := []byte{80, 75, 3, 4, 10, 0, 8, 8, 8, 0, 239, 77, 77, 78,
147,
98, 6, 159, 116, 0, 0, 0, 132, 0, 0, 0, 20, 0, 0, 0, 77, 69, 84, 65,
45, 73, 78, 70, 47, 77, 65, 78, 73, 70, 69, 83, 84, 46, 77, 70, 37,
140, 49, 14, 194, 48, 12, 0, 247, 72, 249, 131, 63, 16, 43, 128, 88,
178, 181, 221, 144, 178, 178, 91, 196, 64, 32, 184, 81, 28, 42, 248,
61,5, 214, 187, 211, 69, 146, 124, 102, 237}
b := bytes.NewReader(buff)
zr := flate.NewReader(b)
if _, err := io.Copy(os.Stdout, zr); err != nil {
log.Fatal(err)
}
if err := zr.Close(); err != nil {
log.Fatal(err)
}
fmt.Println()
Error: flate: corrupt input before offset 5
zlib:
b := bytes.NewReader(buff)
r, err := zlib.NewReader(b)
if err != nil {
panic(err)
}
io.Copy(os.Stdout, r)
r.Close()
Error: zlib: invalid header
Wikipedia says JAR is a extension of zip and normal compression programs can unzip them. Any ideas how to get this working? Like adding a working header or is my code wrong?
JAR files are zip archives, not just zlib or flate compressed data.
So use the archive/zip package to properly process them. For example, to list the files inside the JAR file:
r, err := zip.NewReader(bytes.NewReader(buff), int64(len(buff)))
if err != nil {
panic(err)
}
for _, f := range r.File {
fmt.Println("Found in jar:", f.Name)
}
Of course you have to provide the full file content, else you will most likely get an error when obtaining the zip.Reader.
If you also want to print the contents of the files inside the archive, this is how you could do it:
for _, f := range r.File {
fmt.Printf("Found in jar: %s, contents:\n", f.Name)
rc, err := f.Open()
if err != nil {
log.Fatal(err)
}
_, err = io.CopyN(os.Stdout, rc, int64(f.UncompressedSize64))
if err != nil {
log.Fatal(err)
}
rc.Close()
fmt.Println()
}
Here's a JAR file which contains a single file named a.txt, with contents "Hello Gopher":
buff := []byte{80, 75, 3, 4, 10, 0, 0, 0, 0, 0, 91, 109, 103, 78, 132, 225, 60, 127, 13, 0, 0, 0, 13, 0, 0, 0, 5, 0, 28, 0, 97, 46, 116, 120, 116, 85, 84, 9, 0, 3, 206, 17, 129, 92, 219, 17, 129, 92, 117, 120, 11, 0, 1, 4, 232, 3, 0, 0, 4, 232, 3, 0, 0, 72, 101, 108, 108, 111, 32, 71, 111, 112, 104, 101, 114, 10, 80, 75, 1, 2, 30, 3, 10, 0, 0, 0, 0, 0, 91, 109, 103, 78, 132, 225, 60, 127, 13, 0, 0, 0, 13, 0, 0, 0, 5, 0, 24, 0, 0, 0, 0, 0, 1, 0, 0, 0, 164, 129, 0, 0, 0, 0, 97, 46, 116, 120, 116, 85, 84, 5, 0, 3, 206, 17, 129, 92, 117, 120, 11, 0, 1, 4, 232, 3, 0, 0, 4, 232, 3, 0, 0, 80, 75, 5, 6, 0, 0, 0, 0, 1, 0, 1, 0, 75, 0, 0, 0, 76, 0, 0, 0, 0, 0}
Running the above code on this buffer, the output is (try it on the Go Playground):
Found in jar: a.txt, contents:
Hello Gopher

time.Time Round to Day

I have a timestamp coming in, I wonder if there's a way to round it down to the start of a day in PST. For example, ts: 1305861602 corresponds to 2016-04-14, 21:10:27 -0700, but I want to round it to a timestamp that maps to 2016-04-14 00:00:00 -0700. I read through the time.Time doc but didn't find a way to do it.
The simple way to do this is to create new Time using the previous one and only assigning the year month and day. It would look like this;
func truncateToDay(t time.Time) time.Time {
return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
}
here's a play example; https://play.golang.org/p/jnFuZxruKm
You can simply use duration 24 * time.Hour to truncate time.
t := time.Date(2015, 4, 2, 0, 15, 30, 918273645, time.UTC)
d := 24 * time.Hour
t.Truncate(d)
https://play.golang.org/p/BTz7wjLTWX
I believe the simplest is to create a new date as shown in this answer.
However, if you wanna use time.Truncate, there is two distinct cases.
If you are working in UTC:
var testUtcTime = time.Date(2016, 4, 14, 21, 10, 27, 0, time.UTC)
// outputs 2016-04-14T00:00:00Z
fmt.Println(testUtcTime.Truncate(time.Hour * 24).Format(time.RFC3339))
If you are not, you need to convert back and forth to UTC
var testTime = time.Date(2016, 4, 14, 21, 10, 27, 0, time.FixedZone("my zone", -7*3600))
// this is wrong (outputs 2016-04-14T17:00:00-07:00)
fmt.Println(testTime.Truncate(time.Hour * 24).Format(time.RFC3339))
// this is correct (outputs 2016-04-14T00:00:00-07:00)
fmt.Println(testTime.Add(-7 * 3600 * time.Second).Truncate(time.Hour * 24).Add(7 * 3600 * time.Second).Format(time.RFC3339))
in addition to sticky's answer to get the local Truncate do like this
t := time.Date(2015, 4, 2, 0, 15, 30, 918273645, time.Local)
d := 24 * time.Hour

fmt.Println("in UTC", t.Truncate(d))
_, dif := t.Zone()
fmt.Println("in Local", t.Truncate(24 * time.Hour).Add(time.Second * time.Duration(-dif)))
func truncateToDay(t time.Time) {
nt, _ := time.Parse("2006-01-02", t.Format("2006-01-02"))
fmt.Println(nt)
}
This is not elegant but works.
I using theses functions on all my projects:
package time_utils
import "time"
func TruncateToStartOfDay(t time.Time) time.Time {
return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
}
func TruncateToEndOfDay(t time.Time) time.Time {
return time.Date(t.Year(), t.Month(), t.Day(), 23, 59, 59, 0, t.Location())
}

Golang Sort package - Fuzzy sorting error

I tried to modify standard sorting approach and add certain randomness to sorting Less interface.
when
if (u[i] - u[j]) <= 0
or
if u[i] < u[j]
it works as expected
But
if (u[i] - u[j]) <= rv
condition produces panic after several executions
package main
import (
"crypto/rand"
"fmt"
"math/big"
"sort"
)
type FuzzySorter []float64
func (u FuzzySorter) Len() int {
return len(u)
}
func (u FuzzySorter) Swap(i, j int) {
u[i], u[j] = u[j], u[i]
}
func (u FuzzySorter) Less(i, j int) bool {
pom, _ := rand.Int(rand.Reader, big.NewInt(int64(2)))
rv := float64(pom.Int64())
if (u[i] - u[j]) <= rv {
return true
} else {
return false
}
}
func (u FuzzySorter) Sort() FuzzySorter {
sort.Sort(u)
return u
}
func main() {
unsorted := FuzzySorter{
0,
1,
1,
1,
1,
6,
0,
4,
6,
1,
1,
1,
0,
2,
8,
1,
5,
4,
6,
6,
6,
16,
12,
6,
1,
1,
1,
0,
0,
11,
2,
14,
16,
6,
12,
0,
4,
1,
0,
16,
2,
6,
0,
0,
0,
0,
1,
11,
1,
0,
2,
1,
1,
1,
1,
0,
1,
12,
10,
1,
5,
2,
6,
4,
1,
0,
0,
11,
1,
1,
2,
2,
1,
0,
0,
1,
0,
1,
17,
2,
1,
1,
2,
0,
3,
7,
1,
5,
1,
0,
1,
0,
0,
0,
1,
3,
1,
1,
1,
2,
1,
0,
3,
1,
6,
1,
1,
0,
1,
12,
0,
1,
1,
0,
1,
0,
0,
6,
1,
2,
2,
0,
0,
2,
1,
1,
0,
4,
4,
1,
1,
1,
0,
1,
1,
1,
2,
0,
0,
1,
0,
1,
2,
1,
2,
1,
1,
0,
0,
4,
1,
0,
1,
0,
1,
1,
3,
1,
0,
}
unsorted.Sort()
fmt.Println(unsorted)
}
https://play.golang.org/p/4AxNRN4VD7
panic message
panic: runtime error: index out of range
goroutine 1 [running]:
panic(0x176ba0, 0x1040a010)
/usr/local/go/src/runtime/panic.go:464 +0x700
main.FuzzySorter.Less(0x10456000, 0x9f, 0x9f, 0x19, 0xffffffff, 0x4, 0x1, 0xd)
/tmp/sandbox201242525/main.go:21 +0x140
main.(*FuzzySorter).Less(0x10434140, 0x19, 0xffffffff, 0x5c, 0x1, 0x10434140)
<autogenerated>:3 +0xc0
sort.doPivot(0xfef741b0, 0x10434140, 0x19, 0x9f, 0x7, 0x19)
/usr/local/go/src/sort/sort.go:128 +0x280
sort.quickSort(0xfef741b0, 0x10434140, 0x19, 0x9f, 0xe, 0xfef741b0)
/usr/local/go/src/sort/sort.go:195 +0xa0
sort.Sort(0xfef741b0, 0x10434140)
/usr/local/go/src/sort/sort.go:229 +0x80
main.FuzzySorter.Sort(0x10456000, 0x9f, 0x9f, 0x1, 0x0, 0x0, 0x0, 0x1777a0)
/tmp/sandbox201242525/main.go:29 +0xa0
main.main()
/tmp/sandbox201242525/main.go:195 +0xc0
As far as I can understand Go sort implementation it expect two negative comparisons eg. Less(i, j) and Less(j, i) both return false, which it treats as equality, but not positive. E.g Less(i, j) and Less(j, i) can't both return true. So you can easily achieve desired result logically correct and deterministic way, just
if (u[i] - u[j]) < -1 {
return true
} else {
return false
}
https://play.golang.org/p/VcKI9uzcM9
As of Go 1.8, there is an easier way to sort a slice that does not require you to define new types. You simply create a Less (anonymous) lambda.
a := []int{5, 3, 4, 7, 8, 9}
sort.Slice(a, func(i, j int) bool {
return a[i] < a[j]
})
for _, v := range a {
fmt.Println(v)
}
This will sort in ascending order, if you want the opposite, simply write a[i] < a[j]

Resources