This question already has answers here:
How to sort struct with multiple sort parameters?
(12 answers)
Closed 4 years ago.
I have an array/slice of members:
type SomeType struct {
timeStamp time
typeName string
other variables...
}
And there are 3 methods on this structure based on typeName, like:
isTypeA(): returns bool
isTypeB(): returns bool
isTypeC(): returns bool
Now this is how my sorting needs to work:
- Sort based on timeStamp in ascending order
- If the timeStamp is same, then typeA's should come before typeB's which should come before typeC's.
(type A, B, C is not really alphabetical)
I am using sort.Slice to sort on timeStamp, but I am not sure how to do the type A,B,C sorting.
Translate the arbitrary type values into something that is sortable in the way your requirements ask.
func ThingTypeSortable(thing *Thing) int {
if thing == nil {
return 0
}
if thing.isTypeA() {
return 1
}
if thing.isTypeB() {
return 2
}
if thing.isTypeC() {
return 3
}
return 0
}
...then it's just another thing to sort by if the timestamps are equal.
sort.Slice(inputs, func(i, j int) bool {
if inputs[i].t.UTC() != inputs[j].t.UTC() {
return inputs[i].t.Before(inputs[j].t)
}
return ThingTypeSortable(inputs[i]) < ThingTypeSortable(inputs[j])
})
Use sort.Slice by comparing the fields in sort priority order.
Given variable a of type []SomeType, sort like this:
sort.Slice(a, func(i, j int) bool {
if !a[i].timeStamp.Equal(a[j].timeStamp) {
return a[i].timeStamp.Before(a[j].timeStamp)
}
if a[i].isTypeA() != a[j].isTypeA() {
return a[i].isTypeA()
}
if a[i].isTypeB() != a[j].isTypeB() {
return a[i].isTypeB()
}
return a[i].isTypeC()
})
This code handles isTypeA(), isTypeB() and isTypeC() as separate fields, even though they are derived from the same underlying field.
I assume you meant to use type time.Time for the timeStamp field. The function uses Time.Equal to correctly compare time.Time values for equality.
Related
I have a program that is creating different leaderboards based on different fields in a slice of structs. This requires sorting the slice many times based on different fields where the only thing that is changing in the logic is the field being compared.
Is there a way to encapsulate this repeated logic by passing a field (not its value) as an argument to a function?
For clarification, here is rough approximation of the approach I am taking now:
type Person struct {
Name string
Stat1 int
Stat2 float64
}
person1 := Person{"Alice", 10, 2.0}
person2 := Person{"Bob", 12, 1.0}
person3 := Person{"Zach", 14, 0.1}
people := []Person{person1, person2, person3}
// Sorting based on one field
sort.Slice(people, func(i, j int) bool {
// Comparison logic that depends on comparing the Stat1 field
})
for i, p := range people {
fmt.Println(fmt.Sprint(i+1) + " - " + p.Name + " with " + fmt.Sprint(people[i].Stat1))
}
// Sorting based on another field
sort.Slice(people, func(i, j int) bool {
// Comparison logic that depends on comparing the Stat2 field
// but is otherwise identical to the comparison logic above
})
for i, p := range people {
fmt.Println(fmt.Sprint(i+1) + " - " + p.Name + " with " + fmt.Sprintf("%.1f", people[i].Stat2))
}
// Many, many more of these...
Ideally I'd be able to do something like this (but think this may be impossible without generics):
func sortSliceByField(people []Person, fieldToSortOn PersonField) []Person {
// Repeated sort logic on the field passed as an argument
}
The repetitiveness of the code you displayed would not benefit from the solution you propose. However, you can make it a bit more structured by using something like this:
var SortOptions = map[string]func([]Person) func(int,int)bool {
"stat1": func(people []Person) func(int,int) bool {
return func(i,j int) bool { return people[i].Stat1<people[j].Stat1 }
},
"stat2": func(people []Person) func(int,int) bool {
return func(i,j int) bool { return people[i].Stat2<people[j].Stat2 }
},
}
Then you can do:
sort.Slice(people,SortOptions["stat1"](people))
I want to create a utility-function that is able to merge two given slices, determining equality by a given function.
type IsEqualTest func(interface{}, interface{}) bool
func ArrayMerge(one *[]interface{}, another *[]interface{}, comp IsEqualTest) *[]interface{} {
merged := *one
for _, element := range *another {
if !ArrayContains(one, &element, comp) {
merged = append(merged, element)
}
}
return &merged
}
func ArrayContains(container *[]interface{}, eventualContent *interface{}, comp IsEqualTest) bool {
for _, element := range *container {
if comp(element, eventualContent) {
return true
}
}
return false
}
// please don't mind the algorithmic flaws
However, as go does treat the []interface{} type as non-compatible to slices of anything (and it lacks generics), I would need to iterate over both operands, converting the type of the contained elements when calling, which is not what anyone could want.
What is the Go style of dealing with collections containing any type?
First: without generics, there is no idiomatic way of doing this.
Second: your thinking might be too influenced by other languages. You already got a function to compare, why not take it a bit further?
What I suggest below is not efficient, and it should not be done. However, if you really want to do it:
It looks like this is not a set union, but add the elements of the second slice to the first if they don't already exist in the first slice. To do that, you can pass two functions:
func merge(len1,len2 int, eq func(int,int)bool, write func(int)) {
for i2:=0;i2<len2;i2++ {
found:=false
for i1:=0;i1<len1;i1++ {
if eq(i1,i2) {
found=true
break
}
}
if !found {
write(i2)
}
}
Above, eq(i,j) returns true if slice1[i]==slice2[j], and write(j) does append(result,slice2[j]).
How can I check if two structs that are not of the same type are equal?
Meaning if we have struct of typeA and struct of typeB, if in both structs we have same amount of fields with the same types - they are equal.
type layoutA struct {
A int
}
type layoutB layoutA
reflect.TypeOf(layoutA{}) == reflect.TypeOf(layoutB{}) // false
cmp.Equal(layoutA{}, layoutB{}) // false
compareStructs(layoutA{}, layoutB{}) // true - need to find this function
cmp package -> "github.com/google/go-cmp/cmp"
If the order of the fields needs to match as well you can just check for "convertability" using the ConvertibleTo method of the reflect.Type type.
If the order doesn't matter then you'll have to compare each individual field, a bit more work, but still very basic. First check that both types have the same number of fields, then loop over the fields of one type and check that every field that's present in that type is also present in the other type by using the FieldByName method and then compare the types of those fields.
You can compare the field types by using reflection with something like this:
func compareStructs(s1 interface{}, s2 interface{}) bool {
type1 := reflect.TypeOf(s1)
type2 := reflect.TypeOf(s2)
numField1 := type1.NumField()
numField2 := type2.NumField()
if numField1 != numField2 {
return false
}
for i := 0; i < numField1; i++ {
field1 := type1.Field(i)
field2 := type2.Field(i)
if field1.Type != field2.Type {
return false
}
}
return true
}
I have problem with resolve whether object which was pass as interface to function hasn't initializated fields, like object which was defined as just someObject{} is a empty, because all fields, has value 0, or nil
Problem becomes more complicated if I pass diffrent objects, because each object have diffrent type field value so on this moment I don't find universal way to this.
Example
func main(){
oo := objectOne{}
ot := objectTwo{}
oth := objectThree{"blah" , "balbal" , "blaal"}
resolveIsNotIntialized(oo)
resolveIsNotIntialized(ot)
resolveIsNotIntialized(oth)
}
func resolveIsNotIntialized(v interface{}) bool{
// and below, how resolve that oo and ot is empty
if (v.SomeMethodWhichCanResolveThatAllFiledIsNotIntialized){
return true
}
return false
}
I want to avoid usage switch statement like below, and additional function for each object, ofcorse if is possible.
func unsmartMethod(v interface{}) bool{
switch v.(type){
case objectOne:
if v == (objectOne{}) {
return true
}
// and next object, and next....
}
return false
}
As Franck notes, this is likely a bad idea. Every value is always initialized in Go. Your actual question is whether the type equals its Zero value. Generally the Zero value should be designed such that it is valid. The better approach would generally be to create an interface along the lines of:
type ZeroChecker interface {
IsZero() bool
}
And then attach that to whatever types you want to check. (Or possibly better: create an IsValid() test instead rather than doing your logic backwards.)
That said, it is possible to check this with reflection, by comparing it to its Zero.
func resolveIsNotIntialized(v interface{}) bool {
t := reflect.TypeOf(v)
z := reflect.Zero(t).Interface()
return reflect.DeepEqual(v, z)
}
(You might be able to get away with return v == z here; I haven't thought through all the possible cases.)
I don’t think there is a good reason (in idiomatic Go) to do what you are trying to do. You need to design your structs so that default values (nil, empty string, 0, false, etc.) are valid and represent the initial state of your object. Look at the source of the standard library, there are lots of examples of that.
What you are suggesting is easily doable via Reflection but it will be slow and clunky.
You could narrow the type which your function takes as an argement a little, not take an interface{} but accept one that allows you to check for non-zero values, say type intercae{nonZero() bool} as in the example code below. This will not tell you explicitly that it hasn't been set to the zero value, but that it is not zero.
type nonZeroed interface {
nonZero() bool
}
type zero struct {
hasVals bool
}
func (z zero) nonZero() bool {
return z.hasVals
}
type nonZero struct {
val int
}
func (nz nonZero) nonZero() bool {
return nz.val != 0
}
type alsoZero float64
func (az alsoZero) nonZero() bool {
return az != 0.0
}
func main() {
z := zero{}
nz := nonZero{
val: 1,
}
var az alsoZero
fmt.Println("z has values:", initialized(z))
fmt.Println("nz has values:", initialized(nz))
fmt.Println("az has values:", initialized(az))
}
func initialized(a nonZeroed) bool {
return a.nonZero()
}
Obviously as the type get more complex additional verification would need to be made that it was "nonZero". This type of pattern could be used to check any sort condition.
I've only been working with Go for a couple of days. I have a small variety of different structure types defined, each of which contains a date.
Somehow I need to process those structures in date order, but that ordering has to span across the multiple different structure types. In a dynamically typed language like Python, it's easy to just create a hash of all the objects keyed by date (or hash of lists if they're not unique). In C, I can use unions of pointers or void*. But I'm stuck as to how do this in Go.
I guess I could keep a sorted list of each type and do a manual mergesort as I go. Seems klunky?
What I've read about handling this sort of situation seems to point to using interfaces, but I don't really see how to use them in this situation.
For the sake of argument, let's say I have something like:
type A struct {
Date string
Info string
}
type B struct {
Date string
Info int
}
(Though in practice there are more structures, and they are more complex with multiple fields), and just need to print in date order the contents of an (unsorted) array of each of them.
Is there some way to create a list (date, pointer) pairs to a non-uniform object type?
Per first suggestion below:
package main
import "fmt"
type A struct {
Date string
Info string
}
func (x *A) GetDate() string {
return x.Date
}
type B struct {
Date string
Info int
}
func (x *B) GetDate() string {
return x.Date
}
type Dater interface {
GetDate() string
}
type Daters []Dater
func (s Daters) Len() int { return len(s) }
func (s Daters) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
type ByDate struct{ Daters }
func (s ByDate) Less(i, j int) bool {
return s.Daters[i].GetDate() < s.Daters[j].GetDate()
}
func main() {
// lista and listb are just examples. They really come from elsewhere
lista := []A{{"2012/08/01", "one"}, {"2012/08/03", "three"}}
listb := []B{{"2012/08/02", 2}, {"2012/08/04", 4}}
x := make([]Dater, len(lista) + len(listb))
index := 0
for i := range(lista) {
x[index] = &lista[i]
index++
}
for i := range(listb) {
x[index] = &listb[i]
index++
}
sort.Sort(ByDate{x})
for _,v := range(x) {
fmt.Printf("%#v\n", v)
}
}
That works! So the basic use of interface is fine, and I'm starting to understand
interfaces a little better - thank you!
Note: The creation of x is pretty ugly. I can't see a cleaner / more idiomatic way?
Define a interface (say Dated) with a method (say getDate() that returns Date). Then have all structs (A, B, C) implementing Dated interface. Then you can define use []Dated to hold your type values.
You might want to check package 'time' and 'sort' to simplify the implementation.
You might be able to use embedding.
You would define a struct that contains nothing but the date, then embed it in the others. That way, you only have to implement GetDate() once. You can also extend the Date struct at any time without modifying the other structs.
package main
type Dater interface {
GetDate() string
}
type Date struct {
Date string
}
func (d *Date) GetDate() string {
return d.Date
}
type A struct {
Date
Info string
}
type B struct {
Date
Info []byte
}
type C struct {
Date
Info int32
}
You can now call GetDate() on A, B, and C.