dragon8837 2017-06-03 11:15
浏览 90

如何平均JSON数组的值,然后舍入到小数点后1位

I'm new to Go and can't figure out how to simply read and average the values of an array of JSONs. I also want to round my result to 1 decimal point, but Go has no Round() function. Here's the data:

[
    {"millisUTC":"1496424000000","price":"7.6"},
    {"millisUTC":"1496423700000","price":"7.5"},
    {"millisUTC":"1496423400000","price":"9.1"},
    {"millisUTC":"1496423100000","price":"9.2"},
    {"millisUTC":"1496422800000","price":"10.0"}
]

I want to get the prices and average them, rounding to 1 decimal point. Yet it took me over 30 lines of code, when (as a Ruby developer) it would usually take me 3 lines. How do I simplify this? My code takes in 2 parameters, starttime and endtime, and calls an API: https://github.com/rayning0/griddy/blob/master/controllers/default.go

type arrayOfMaps []map[string]string

func getAvgPrice(starttime, endtime string) float64 {
    response, err := http.Get("https://hourlypricing.comed.com/api?type=5minutefeed&datestart=" + starttime + "&dateend=" + endtime)

   if err != nil {
        fmt.Println(err)
    }
    defer response.Body.Close()

    energyJSON, err := ioutil.ReadAll(response.Body)
    if err != nil {
        fmt.Println(err)
    }

    var energyPrices arrayOfMaps
    err = json.Unmarshal(energyJSON, &energyPrices)

    fmt.Println("Energy prices between", starttime, "and", endtime)
    fmt.Println(energyPrices)

    var sum float64
    var size int
    for _, p := range energyPrices {
        price, _ := strconv.ParseFloat(p["price"], 64)
        sum += price
        size++
    }
    avg := Truncate(sum / float64(size))
    fmt.Println("Average price:", avg)
    return avg
}

//Truncate a float to 1 level of precision
func Truncate(some float64) float64 {
    return float64(int(some*10)) / 10
}

Edited, thanks to excellent help from @icza!

This applies to my question: https://golang.org/pkg/encoding/json/#Decoder.Decode

See my revised solution, with detailed comments: https://github.com/rayning0/griddy/blob/master/controllers/default.go

  • 写回答

1条回答 默认 最新

  • duanlei0282 2017-06-03 14:34
    关注

    Your code can be simplified in several points, and while you said "rounding" you did "truncating" in the end which is not the same.

    One important thing: if an error is encountered, you should return early and not continue, as that will only be the source of additional errors or even runtime panics. See at the end.

    Unmarshaling JSON

    Easier would be to use json.Decoder, decoding right from the response body (which implements io.Reader).

    Also note to simplify parsing floats given as string values in JSON, a better option would be to use json.Number.

    Parsing can be as simple as this:

    var prices []map[string]json.Number
    if err := json.NewDecoder(response.Body).Decode(&prices); err != nil {
        fmt.Println(err)
        return
    }
    

    Calculating sum

    Calculating sum can also be simplified: there is no need to keep track of size, as that is simply the length of the map:

    sum := 0.0
    for _, p := range prices {
        f, _ := p["price"].Float64()
        sum += f
    }
    

    Note that if you want to handle errors in a way to simply exclude it from the sum (and not return with an error), only then would you need to count valid numbers.

    Rounding

    Multiplying by 10 and then dividing by 10 is truncating and not rounding. For rounding, you should add 0.5 between those 2 operations. For details, see this answer: Golang Round to Nearest 0.05

    So a correct rounding function that properly rounds both positive and negative numbers to arbitrary unit:

    func Round(x, unit float64) float64 {
        if x > 0 {
            return float64(int64(x/unit+0.5)) * unit
        }
        return float64(int64(x/unit-0.5)) * unit
    }
    

    So the result:

    avg := Round(sum/float64(len(prices)), 0.1)
    

    The complete solution with error handling

    Since your getAvgPrice() can fail at multiple points, you should definitely add an error return value too.

    This is the complete solution with proper error handling:

    func getAvgPrice(starttime, endtime string) (float64, error) {
        response, err := http.Get("https://hourlypricing.comed.com/api?type=5minutefeed&datestart=" + starttime + "&dateend=" + endtime)
        if err != nil {
            return 0, err
        }
        defer response.Body.Close()
    
        var prices []map[string]json.Number
        if err := json.NewDecoder(response.Body).Decode(&prices); err != nil {
            return 0, err
        }
    
        sum := 0.0
        for _, p := range prices {
            f, err := p["price"].Float64()
            if err != nil {
                return 0, err
            }
            sum += f
        }
    
        return Round(sum/float64(len(prices)), 0.1), nil
    }
    
    评论

报告相同问题?

悬赏问题

  • ¥15 Oracle中如何从clob类型截取特定字符串后面的字符
  • ¥15 想通过pywinauto自动电机应用程序按钮,但是找不到应用程序按钮信息
  • ¥15 MATLAB中streamslice问题
  • ¥15 如何在炒股软件中,爬到我想看的日k线
  • ¥15 51单片机中C语言怎么做到下面类似的功能的函数(相关搜索:c语言)
  • ¥15 seatunnel 怎么配置Elasticsearch
  • ¥15 PSCAD安装问题 ERROR: Visual Studio 2013, 2015, 2017 or 2019 is not found in the system.
  • ¥15 (标签-MATLAB|关键词-多址)
  • ¥15 关于#MATLAB#的问题,如何解决?(相关搜索:信噪比,系统容量)
  • ¥500 52810做蓝牙接受端