我是Go的新手,无法弄清楚如何简单地读取和平均JSON数组的值。我还想将结果舍入到1个小数点,但Go没有Round()
函数。这是数据:
[
{"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"}
]
我想得到价格并将它们平均,四舍五入到小数点后1位。然而,当我(作为Ruby开发人员)通常需要3行代码时,我花了30多行代码。我该如何简化这个?我的代码包含2个参数,starttime
和endtime
,并调用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
}
编辑,感谢@icza的出色帮助!
这适用于我的问题:https://golang.org/pkg/encoding/json/#Decoder.Decode
请参阅我的修订解决方案,并附上详细评论:https://github.com/rayning0/griddy/blob/master/controllers/default.go
答案 0 :(得分:2)
你的代码可以在几个方面简化,当你说“四舍五入”时你最后“截断”了不一样。
一件重要的事情:如果遇到错误,您应该提前返回而不是继续,因为这只会是其他错误的来源,甚至是运行时恐慌。见最后。
更容易使用json.Decoder
,直接从响应正文解码(实现io.Reader
)。
另请注意,要简化解析JSON中string
值给出的浮点数,更好的选择是使用json.Number
。
解析可以这么简单:
var prices []map[string]json.Number
if err := json.NewDecoder(response.Body).Decode(&prices); err != nil {
fmt.Println(err)
return
}
计算总和也可以简化:无需跟踪size
,因为这只是地图的长度:
sum := 0.0
for _, p := range prices {
f, _ := p["price"].Float64()
sum += f
}
请注意,如果您想以一种简单的方式将错误从总和中排除(并且不会返回错误),那么您才需要计算有效数字。
乘以10然后除以10是截断而不是舍入。对于舍入,您应该在这两个操作之间添加0.5
。有关详细信息,请参阅此答案:Golang Round to Nearest 0.05
这是一个正确的舍入函数,可以将正数和负数正确地舍入到任意单位:
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
}
结果是:
avg := Round(sum/float64(len(prices)), 0.1)
由于您的getAvgPrice()
可能会在多个点失败,因此您也一定要添加错误返回值。
这是具有正确错误处理的完整解决方案:
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
}