lunix
lunix

Reputation: 323

How to order a list of different struct types in go

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?

Upvotes: 4

Views: 915

Answers (2)

Thomas Kappler
Thomas Kappler

Reputation: 4115

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.

Upvotes: 0

Greenblade
Greenblade

Reputation: 90

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.

Upvotes: 3

Related Questions