如何使用 Golang 在结构切片中找到最常见的整数

how to find most frequent integer in a slice of struct with Golang

提问人:ariejanuar 提问时间:9/30/2022 更新时间:9/30/2022 访问量:371

问:

免责声明:我不是专业开发人员,一直在修补 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

json 字典 go 切片 解码

评论

2赞 torek 9/30/2022
对数组进行排序并选择中间元素会得到中位数,这可能不是最常见的:考虑 [1,1,1,1,2,3,4,5,6],它已经排序并且中位数为 2,但最常见的是 1。这类问题有两个不同的方面:寻找或选择一种算法,然后用任何给定的语言实现该算法......

答:

-1赞 Nicholas Carey 9/30/2022 #1

应该比

  • 按年龄和名称对源切片进行排序
  • 将其分解为具有共同年龄的序列,然后
  • 在进行过程中,跟踪最常见的

像这样的东西:

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
}
2赞 MrChelou 9/30/2022 #2

在实施解决方案之前要执行的操作

在我上大学的头几年,我的老师总是会向我和我的同学重复一些事情,不要先写代码,特别是如果你是初学者,请按照以下步骤操作:

  • 写下你想要发生的事情
  • 将问题详细说明为小步骤
  • 在分支时编写所有方案和案例
  • 写入输入和输出(方法/函数签名)
  • 检查它们是否相互契合

让我们按照以下步骤操作...

写下你想要发生的事情

你已经很好地定义了你的问题,所以我将跳过这一步。

让我们进一步详细说明这一点

  1. 您有一份乘客名单
  2. 您希望按年龄对乘客进行分组
  3. 你想看看哪些是最常见的/哪些乘客最多。
  4. 您要按字母顺序打印名称

分支

  • 场景一:一个组的规模大于所有其他组。
  • 方案二:两个或多个组的规模相同,并且比其他组大。

可能还有更多场景,但它们是你找到的

输入输出 ??

好了,既然我们已经知道了我们必须做什么,我们将检查每个步骤的输入和输出以实现此目标。

步骤:

  1. 您有一份乘客名单
  • input => none 或 filename (string)
  • 输出 => []乘客
  1. 您希望按年龄对乘客进行分组
  • input => []乘客 // 乘客名单
  • output => map[int][]int or map[int][]&Passenger // 年龄组

第一种类型,括号内的类型是整个群体的年龄。

第二种类型是包含以下任一内容的列表:

  • 乘客在列表中的位置
  • 内存中对象/乘客的地址

这并不重要,只要我们能轻松地从列表中找回乘客,而无需再次迭代即可。

  1. 你想看看哪些是最常见的/哪些乘客最多。
  • input => 组 (ageGroups)

所以在这里,我们有场景 1 和 2 分支......这意味着它必须对所有方案都有效,或者使用条件来分支它们。

  • 方案 1 的输出 => 最常见年龄 (int)
  • 方案 2 的输出 => 个最常见的年龄 ([]int)

我们可以看到,场景 1 的输出可以与场景 2 的输出合并

  1. 您想按字母顺序打印所有乘客的姓名 年龄组

    • 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("}")
    }
}


评论

0赞 cs1349459 10/4/2022
刚刚回顾了这个,绝对惊人的第一个答案!我只希望每个人都能做出这样的回答......