使用Go在片段结构中按时间平均

问题描述:

I'm averaging values by hour in a slice of structs in a basic way, and I would get a better aproach to it in order to get a most generic funcion that can get averaging by hours, days, weeks, etc. Thanks in advance to everybody.

package main

import (
    "fmt"
    "math/rand"
    "time"
)

type Acc struct {
    name  string
    money int
    date time.Time
}

type Accs []Acc

const Tformat = "02/01/2006 15:04:05"

func main() {
    var myaccs Accs
    acc := 0
    var loops int
    var hour int
    f1, _ := time.Parse(Tformat, "29/08/2013 00:00:19")
    // Creating a Slice of structs 
    for i := 0; i < 10; i++ { 
        f1 = f1.Add(20 * time.Minute) //adding 20 minutes to every record
        myaccs = append(myaccs, Acc{name: "christian", money: rand.Intn(200), date: f1})
        fmt.Printf("Added to slice: %v, %d, %s
", myaccs[i].name, myaccs[i].money, myaccs[i].date)
    }
    // Averaging 
    for _, v := range myaccs {
        if acc == 0 {
            hour = v.date.Hour()
            acc += v.money
            loops++
        } else {
            if v.date.Hour() == hour {
                acc += v.money
                loops++
            } else {
                fmt.Printf("Average money value to hour %d : %d
", hour, acc / loops) //->Action

                acc = v.money
                hour = v.date.Hour()
                loops = 1
            }
        }
        //fmt.Println(v, acc, loops, hour)
    }
    fmt.Printf("Average money value to hour %d : %d
", hour, acc / loops)//->Action
}

Note: Money variable is a int like a example only..
Note2: I'm considering that the data are already sorted
Playground: http://play.golang.org/p/lL3YDD4ecE

Time math is frought with peril, but here is one way to approach the problem:

type Snapshot struct {
    Value AccountValue
    At    time.Time
}

type Granularity struct {
    Name          string
    DateIncrement [3]int
    DurIncrement  time.Duration
    DateFormat    string
}

type Graph struct {
    Granularity
    Values map[string][]AccountValue
}

func (g *Graph) Add(snaps []Snapshot) {
    if g.Values == nil {
        g.Values = map[string][]AccountValue{}
    }
    for _, s := range snaps {
        key := g.Format(s.At)
        g.Values[key] = append(g.Values[key], s.Value)
    }
}

func (g *Graph) Get(from, to time.Time) (snaps []Snapshot) {
    from, to = g.Truncate(from), g.Truncate(to)
    for cur := from; !to.Before(cur); cur = g.AddTo(cur) {
        var avg, denom AccountValue
        for _, v := range g.Values[g.Format(cur)] {
            avg += v
            denom += 1
        }
        if denom > 0 {
            avg /= denom
        }
        snaps = append(snaps, Snapshot{
            Value: avg,
            At:    cur,
        })
    }
    return snaps
}

Full code in Playground

For unsorted data

Start by implementing the sort interface on the type Accs []Acc. Then you can easily sort by hours, days, weeks.

For sorted data

Create a GroupBy method on Accs.

func (accs Accs) GroupBy(p func(Acc,Acc) bool) [][]Accs {
    // your looping/comparing/grouping code goes here
}

Use the predicate function p to pass in group specific code for comparing two Acc structs to see if they should go in the same group.

Once the Accs are in groups you can sum, avg etc.