英文:
golang: How can I populate a multi-struct map in a loop?
问题
我有客户与 API 的交互日志文件。我想解析这些日志并将结果输入到一个结构体映射中,以便我可以将数据组织成有用的信息。例如,我想回答以下查询:"显示每个用户每天的请求总数"。
我已经创建了一个似乎足够的数据结构来保存数据。然而,当我尝试运行程序时,我得到了错误:invalid operation: dates[fields[1]] (type *Dates does not support indexing) [process exited with non-zero status]
。
有没有更好的数据结构可以使用?或者有没有办法使这个数据结构适用于这个特定的目的?
以下是要翻译的代码部分:
package main
import (
"fmt"
"strings"
)
type Stats struct {
totalNumberOfRequests int
}
type Customer struct {
listOfCustomers map[string]Stats // map[customerid]Stats
}
type Dates struct {
listOfDates map[string]Customer // map[date]Customer
}
var requestLog = []string{
"2011-10-05, 1234, apiquery",
"2011-10-06, 1234, apiquery",
"2011-10-06, 5678, apiquery",
"2011-10-09, 1234, apiquery",
"2011-10-12, 1234, apiquery",
"2011-10-13, 1234, apiquery",
}
func main() {
dates := new(Dates)
for _, entry := range requestLog {
fmt.Println("entry:", entry)
fields := strings.Split(entry, ", ")
dates.listOfDates[fields[0]].listOfCustomers[fields[1]].totalNumberOfRequests++
}
}
请注意,我只会返回翻译好的代码部分,不会回答关于翻译的问题。
英文:
I have log files of customer interactions with an API. I want to parse those logs and feed the results into a map of structs so that I can organize the data into helpful information. For example, I would like to respond to the following query: "show me the total number of requests per user per day".
I have created what seems like an adequate structure to hold the data. However, when I try to run the program I get the error: invalid operation: dates[fields[1]] (type *Dates does not support indexing) [process exited with non-zero status]
.
http://play.golang.org/p/8u3jX26ktt
package main
import (
"fmt"
"strings"
)
type Stats struct {
totalNumberOfRequests int
}
type Customer struct {
listOfCustomers map[string]Stats // map[customerid]Stats
}
type Dates struct {
listOfDates map[string]Customer // map[date]Customer
}
var requestLog = []string{
"2011-10-05, 1234, apiquery",
"2011-10-06, 1234, apiquery",
"2011-10-06, 5678, apiquery",
"2011-10-09, 1234, apiquery",
"2011-10-12, 1234, apiquery",
"2011-10-13, 1234, apiquery",
}
func main() {
dates := new(Dates)
for _, entry := range requestLog {
fmt.Println("entry:", entry)
fields := strings.Split(entry, "'")
dates.listOfDates[fields[0]].listOfCustomers[fields[1]].totalNumberOfRequests++
}
}
Is there a better structure to use? Or is there a way to make this structure work for this particular purpose?
答案1
得分: 1
如果我理解你对输出的期望,这里有一个解决方案。然而,我不喜欢"Customer is a map with id and Stat.. I think it should be a simpler struct with two Fields (cid string
and stat Stats
). Also the Dates structure does not allow for multiple customers on the same date, so I've changed to map single date to list of users."这句话。我认为它应该是一个更简单的结构,有两个字段(cid string
和stat Stats
)。另外,Dates结构不允许在同一天有多个客户,所以我将其更改为将单个日期映射到用户列表的方式。
我还添加了更多的"测试场景",以覆盖客户在同一天多次访问资源的情况。
你似乎没有使用你的示例中的"apiquery",所以下面的代码与之不匹配。
关于在结构体中更改为指针的问题,请参阅此问题(如你的问题的评论中所提到的)。
package main
import (
"fmt"
"strings"
)
type Stats struct {
totalNumberOfRequests int
}
type Customer struct {
customerWithStat map[string]*Stats // a customer with it's corresponding stats
}
type Dates struct {
listOfDates map[string][]*Customer // map[date]list of customers (for each date)
}
var requestLog = []string{
"2011-10-05, 1234, apiquery",
"2011-10-06, 5678, apiquery",
"2011-10-06, 1234, apiquery",
"2011-10-06, 1234, apiquery",
"2011-10-06, 5678, apiquery",
"2011-10-06, 1234, apiquery",
"2011-10-09, 1234, apiquery",
"2011-10-12, 1234, apiquery",
"2011-10-13, 1234, apiquery",
"2011-10-13, 1234, apiquery",
"2011-10-06, 1234, apiquery",
}
func main() {
listOfDates := make(map[string][]*Customer)
dates := Dates{listOfDates}
for _, entry := range requestLog {
fields := strings.Split(entry, ",")
curDateStr := strings.TrimSpace(fields[0])
curCustIdStr := strings.TrimSpace(fields[1])
if customersAtDate, dateExists := dates.listOfDates[curDateStr]; dateExists {
// Date already exist
for _, curCustomer := range customersAtDate {
if curStat, customerExists := curCustomer.customerWithStat[curCustIdStr]; customerExists {
// The user has already accessed this resource - just increment
curStat.totalNumberOfRequests++
} else {
// New user - set access to 1
curCustomer.customerWithStat[curCustIdStr] = &Stats{1}
}
}
} else {
// New Date
// Init the Statistic for the new customer
newCustomerData := make(map[string]*Stats)
newCustomerData[curCustIdStr] = &Stats{1}
// Create the customer itself
newCustomer := &Customer{newCustomerData}
// add to the current day list
dates.listOfDates[curDateStr] = append(dates.listOfDates[curDateStr], newCustomer)
}
}
// Print result
for date, customers := range dates.listOfDates {
fmt.Println("Date: ", date)
for _, customer := range customers {
for cid, stat := range customer.customerWithStat {
fmt.Println(" Customer: ", cid)
fmt.Println(" # Requests: ", *stat)
}
}
}
}
这将输出:
Date: 2011-10-05
Customer: 1234
# Requests: {1}
Date: 2011-10-06
Customer: 5678
# Requests: {2}
Customer: 1234
# Requests: {4}
Date: 2011-10-09
Customer: 1234
# Requests: {1}
Date: 2011-10-12
Customer: 1234
# Requests: {1}
Date: 2011-10-13
Customer: 1234
# Requests: {2}
英文:
If I understood your expectations about the output here's a solution. However I don't like that "Customer is a map with id and Stat.. I think it should be a simpler struct with two Fields (cid string
and stat Stats
). Also the Dates structure does not allow for multiple customers on the same date, so I've changed to map single date to list of users.
I've also added more "tests scenarios" to cover the cases for a customer accessing the resource multiple times on the same date.
You don't seem to use the "apiquery" your example, so the code below does not match against it.
Regarding the change to pointers in the structs - see this issue (as noted in the comments to your question)
package main
import (
"fmt"
"strings"
)
type Stats struct {
totalNumberOfRequests int
}
type Customer struct {
customerWithStat map[string]*Stats // a customer with it's corresponding stats
}
type Dates struct {
listOfDates map[string][]*Customer // map[date]list of customers (for each date)
}
var requestLog = []string{
"2011-10-05, 1234, apiquery",
"2011-10-06, 5678, apiquery",
"2011-10-06, 1234, apiquery",
"2011-10-06, 1234, apiquery",
"2011-10-06, 5678, apiquery",
"2011-10-06, 1234, apiquery",
"2011-10-09, 1234, apiquery",
"2011-10-12, 1234, apiquery",
"2011-10-13, 1234, apiquery",
"2011-10-13, 1234, apiquery",
"2011-10-06, 1234, apiquery",
}
func main() {
listOfDates := make(map[string][]*Customer)
dates := Dates{listOfDates}
for _, entry := range requestLog {
fields := strings.Split(entry, ",")
curDateStr := strings.TrimSpace(fields[0])
curCustIdStr := strings.TrimSpace(fields[1])
if customersAtDate, dateExists := dates.listOfDates[curDateStr]; dateExists {
// Date already exist
for _, curCustomer := range customersAtDate {
if curStat, customerExists := curCustomer.customerWithStat[curCustIdStr]; customerExists {
// The user has already accessed this resource - just increment
curStat.totalNumberOfRequests++
} else {
// New user - set access to 1
curCustomer.customerWithStat[curCustIdStr] = &Stats{1}
}
}
} else {
// New Date
// Init the Statistic for the new customer
newCustomerData := make(map[string]*Stats)
newCustomerData[curCustIdStr] = &Stats{1}
// Create the customer itself
newCustomer := &Customer{newCustomerData}
// add to the current day list
dates.listOfDates[curDateStr] = append(dates.listOfDates[curDateStr], newCustomer)
}
}
// Print result
for date, customers := range dates.listOfDates {
fmt.Println("Date: ", date)
for _, customer := range customers {
for cid, stat := range customer.customerWithStat {
fmt.Println(" Customer: ", cid)
fmt.Println(" # Requests: ", *stat)
}
}
}
}
This will output:
Date: 2011-10-05
Customer: 1234
# Requests: {1}
Date: 2011-10-06
Customer: 5678
# Requests: {2}
Customer: 1234
# Requests: {4}
Date: 2011-10-09
Customer: 1234
# Requests: {1}
Date: 2011-10-12
Customer: 1234
# Requests: {1}
Date: 2011-10-13
Customer: 1234
# Requests: {2}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论