提问人:ariejanuar 提问时间:9/30/2022 更新时间:9/30/2022 访问量:371
如何使用 Golang 在结构切片中找到最常见的整数
how to find most frequent integer in a slice of struct with Golang
问:
免责声明:我不是专业开发人员,一直在修补 Golang 大约 8 个月(Udemy + YouTube),但仍然不知道如何解决如下所示的简单问题。.
以下是问题的摘要:
我正在尝试从来自解码.json文件的结构中找到最常见的“年龄”(包含字符串“name”和整数“age”)。
之后,我需要根据“年龄”的最大出现频率打印“名称”。
基于“年龄”最大出现的打印“名称”需要按 alpabetheth 排序
输入 (.json) :
[
{"name": "John","age": 15},
{"name": "Peter","age": 12},
{"name": "Roger","age": 12},
{"name": "Anne","age": 44},
{"name": "Marry","age": 15},
{"name": "Nancy","age": 15}
]
输出 : ['John', 'Mary', 'Nancy'].
解释:因为数据中出现次数最多的年龄是 15 岁(出现 3 次),所以输出应该是带有三个人的字符串数组 name,在本例中应为 ['John', 'Mary', 'Nancy']。
例外:
- 如果有多个“年龄”具有相同的最大出现次数,我需要拆分数据并将它们打印成不同的数组(即当“Anne”年龄为 12 岁时,结果是:['John', 'Mary', 'Nancy'], ['Anne','Peter','Roger']
这是我尝试过的(在 Golang 中):
package main
{
import (
"encoding/json"
"fmt"
"os"
"sort"
)
// 1. preparing the empty struct for .json
type Passanger struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
// 2. load the json file
content, err := os.ReadFile("passanger.json")
if err != nil {
fmt.Println(err.Error())
}
// 3. parse json file to slice
var passangers []Passanger
err2 := json.Unmarshal(content, &passangers)
if err2 != nil {
fmt.Println("Error JSON Unmarshalling")
fmt.Println(err2.Error())
}
// 4. find most frequent age numbers (?)
for _, v := range passangers {
// this code only show the Age on every line
fmt.Println(v.Age)
}
// 5. print the name and sort them apabethically (?)
// use sort.slice package
// implement group based by "max-occurence-age"
}
从昨天开始就被卡住了,我也尝试从许多编码挑战问题中实现解决方案,例如:
func majorityElement(arr int) int {
sort.Ints(arr)
return arr[len(arr)/2]
}
但我仍然在努力理解如何处理 Passanger 切片中的“年龄”值作为上面代码的整数输入 (arr int)。
我在网上找到的其他解决方案是通过迭代 trough map[int]int 来找到最大频率:
func main(){
arr := []int{90, 70, 30, 30, 10, 80, 40, 50, 40, 30}
freq := make(map[int]int)
for _ , num := range arr {
freq[num] = freq[num]+1
}
fmt.Println("Frequency of the Array is : ", freq)
}
但话又说回来,.json文件不仅包含整数(age),还包含字符串(name)格式,我仍然不知道如何分别处理“name”和“age”。
我真的需要一个适当的指导。
这是我上面提到的源代码(main.go)和(.json)文件: https://github.com/ariejanuarb/golang-json
答:
应该比
- 按年龄和名称对源切片进行排序
- 将其分解为具有共同年龄的序列,然后
- 在进行过程中,跟踪最常见的
像这样的东西:
https://goplay.tools/snippet/6pCpkTEaDXN
type Person struct {
Age int
Name string
}
func MostCommonAge(persons []Person) (mostCommonAge int, names []string) {
sorted := make([]Person, len(persons))
copy(sorted, persons)
// sort the slice by age and then by name
sort.Slice(sorted, func(x, y int) bool {
left, right := sorted[x], sorted[y]
switch {
case left.Age < right.Age:
return true
case left.Age > right.Age:
return false
default:
return left.Name < right.Name
}
})
updateMostCommonAge := func(seq []Person) (int, []string) {
if len(seq) > len(names) {
buf := make([]string, len(seq))
for i := 0; i < len(seq); i++ {
buf[i] = seq[i].Name
}
mostCommonAge = seq[0].Age
names = buf
}
return mostCommonAge, names
}
for lo, hi := 0, 0; lo < len(sorted); lo = hi {
for hi = lo; hi < len(sorted) && sorted[lo].Age == sorted[hi].Age; {
hi++
}
mostCommonAge, names = updateMostCommonAge(sorted[lo:hi])
}
return mostCommonAge, names
}
另一种方法使用更多内存,但更简单一些。在这里,我们按年龄构建一个名字地图,然后遍历它以找到具有最长名字列表的键。
https://goplay.tools/snippet/_zmMys516IM
type Person struct {
Age int
Name string
}
func MostCommonAge(persons []Person) (mostCommonAge int, names []string) {
namesByAge := map[int][]string{}
for _, p := range persons {
value, found := namesByAge[p.Age]
if !found {
value = []string{}
}
namesByAge[p.Age] = append(value, p.Name)
}
for age, nameList := range namesByAge {
if len(nameList) > len(names) {
mostCommonAge, names = age, nameList
}
}
return mostCommonAge, names
}
在实施解决方案之前要执行的操作
在我上大学的头几年,我的老师总是会向我和我的同学重复一些事情,不要先写代码,特别是如果你是初学者,请按照以下步骤操作:
- 写下你想要发生的事情
- 将问题详细说明为小步骤
- 在分支时编写所有方案和案例
- 写入输入和输出(方法/函数签名)
- 检查它们是否相互契合
让我们按照以下步骤操作...
写下你想要发生的事情
你已经很好地定义了你的问题,所以我将跳过这一步。
让我们进一步详细说明这一点
- 您有一份乘客名单
- 您希望按年龄对乘客进行分组
- 你想看看哪些是最常见的/哪些乘客最多。
- 您要按字母顺序打印名称
分支
- 场景一:一个组的规模大于所有其他组。
- 方案二:两个或多个组的规模相同,并且比其他组大。
可能还有更多场景,但它们是你找到的
输入输出 ??
好了,既然我们已经知道了我们必须做什么,我们将检查每个步骤的输入和输出以实现此目标。
步骤:
- 您有一份乘客名单
- input => none 或 filename (string)
- 输出 => []乘客
- 您希望按年龄对乘客进行分组
- input => []乘客 // 乘客名单
- output => map[int][]int or map[int][]&Passenger // 年龄组
第一种类型,括号内的类型是整个群体的年龄。
第二种类型是包含以下任一内容的列表:
- 乘客在列表中的位置
- 内存中对象/乘客的地址
这并不重要,只要我们能轻松地从列表中找回乘客,而无需再次迭代即可。
- 你想看看哪些是最常见的/哪些乘客最多。
- input => 组 (ageGroups)
所以在这里,我们有场景 1 和 2 分支......这意味着它必须对所有方案都有效,或者使用条件来分支它们。
- 方案 1 的输出 => 最常见年龄 (int)
- 方案 2 的输出 => 个最常见的年龄 ([]int)
我们可以看到,场景 1 的输出可以与场景 2 的输出合并
您想按字母顺序打印所有乘客的姓名 年龄组
- input =>组 ([]乘客) + 年龄 ([]int) + 乘客名单 ([]乘客)。
- 输出 => 字符串或 []字节,或者如果你只是打印它,什么都没有......
老实说,如果你愿意,你可以跳过这个。
检查后,编码时间
让我们首先创建符合我们签名的函数
type Passenger struct {
Name string `json:"name"`
Age int `json:"age"`
}
func GetPassengerList() []Passenger{
// 2. load the json file
content, err := os.ReadFile("passanger.json")
if err != nil {
fmt.Println(err.Error())
}
// 3. parse json file to slice
var passengers []Passenger
err2 := json.Unmarshal(content, &passengers)
if err2 != nil {
fmt.Println("Error JSON Unmarshalling")
fmt.Println(err2.Error())
}
return passengers
}
// 4a. Group by Age
func GroupByAge(passengers []Passenger) map[int][]int {
group := make(map[int][]int, 0)
for index, passenger := range passengers {
ageGroup := group[passenger.Age]
ageGroup = append(ageGroup, index)
group[passenger.Age] = ageGroup
}
return group
}
// 4b. find the most frequent age numbers
func FindMostCommonAge(ageGroups map[int][]int) []int {
mostFrequentAges := make([]int, 0)
biggestGroupSize := 0
// find most frequent age numbers
for age, ageGroup := range ageGroups {
// is most frequent age
if biggestGroupSize < len(ageGroup) {
biggestGroupSize = len(ageGroup)
mostFrequentAges = []int{age}
} else if biggestGroupSize == len(ageGroup) { // is one of the most frequent age
mostFrequentAges = append(mostFrequentAges, age)
}
// is not one of the most frequent age so does nothing
}
return mostFrequentAges
}
func main() {
passengers := loadPassengers()
// I am lazy but if you want you could sort the
// smaller slice before printing to increase performance
sort.Slice(passengers, func(i, j int) bool {
if passengers[i].Age == passengers[j].Age {
return passengers[i].Name < passengers[j].Name
}
return passengers[i].Age < passengers[j].Age
})
// age => []position
// Length of the array count as the number of occurences
ageGrouper := GroupByAge(passengers)
mostFrequentAges := FindMostCommonAge(ageGrouper)
// print the passenger
for _, age := range mostFrequentAges {
fmt.Println("{")
for _, passengerIndex := range ageGrouper[age] {
fmt.Println("\t", passengers[passengerIndex].Name)
}
fmt.Println("}")
}
}
评论