英文:
Combine different sort in Go
问题
如何在Go中组合排序器?例如,首先按评论数量排序,但如果评论数量为空,则按字母顺序排序。
这是我尝试过的代码:
func sortArticles(articles []*Article) []*Article {
topArticlesSlice := make([]*Article, 0)
topArticlesSlice = append(topArticlesSlice, articles[:]...)
sort.SliceStable(topArticlesSlice, func(i, j int) bool {
var sortedByNumComments, areNumCommentsEquals, sortedByName bool
if topArticlesSlice[i].NumComments != nil && topArticlesSlice[j].NumComments != nil {
areNumCommentsEquals = *topArticlesSlice[i].NumComments == *topArticlesSlice[j].NumComments
sortedByNumComments = *topArticlesSlice[i].NumComments > *topArticlesSlice[j].NumComments
}
if areNumCommentsEquals {
if topArticlesSlice[i].Title != nil && topArticlesSlice[j].Title != nil {
sortedByName = *topArticlesSlice[i].Title == *topArticlesSlice[j].Title
return sortedByName
} else if topArticlesSlice[i].StoryTitle != nil && topArticlesSlice[j].StoryTitle != nil {
sortedByName = *topArticlesSlice[i].StoryTitle == *topArticlesSlice[j].StoryTitle
return sortedByName
}
return false
}
return sortedByNumComments
})
return topArticlesSlice
}
我的结构体(https://go.dev/play/p/27j-sFKaG2M)
type ArticleResponse struct {
Page int `json:"page"`
PerPage int `json:"per_page"`
Total int `json:"total"`
TotalPages int `json:"total_pages"`
Articles []*Article `json:"data"`
}
type Article struct {
Title *string `json:"title"`
URL *string `json:"url"`
Author string `json:"author"`
NumComments *int `json:"num_comments"`
StoryID interface{} `json:"story_id"`
StoryTitle *string `json:"story_title"`
StoryURL *string `json:"story_url"`
ParentID *int `json:"parent_id"`
CreatedAt int `json:"created_at"`
}
英文:
How can I combine sorters in Go? For example first I need sort by number of comments but if number of comments is null I need sort alphabetically.
This is what I have tried.
func sortArticles(articles []*Article) []*Article {
topArticlesSlice := make([]*Article, 0)
topArticlesSlice = append(topArticlesSlice, articles[:]...)
sort.SliceStable(topArticlesSlice, func(i, j int) bool {
var sortedByNumComments, areNumCommentsEquals, sortedByName bool
if topArticlesSlice[i].NumComments != nil && topArticlesSlice[j].NumComments != nil {
areNumCommentsEquals = *topArticlesSlice[i].NumComments == *topArticlesSlice[j].NumComments
sortedByNumComments = *topArticlesSlice[i].NumComments > *topArticlesSlice[j].NumComments
}
if areNumCommentsEquals {
if topArticlesSlice[i].Title != nil && topArticlesSlice[j].Title != nil {
sortedByName = *topArticlesSlice[i].Title == *topArticlesSlice[j].Title
return sortedByName
} else if topArticlesSlice[i].StoryTitle != nil && topArticlesSlice[j].StoryTitle != nil {
sortedByName = *topArticlesSlice[i].StoryTitle == *topArticlesSlice[j].StoryTitle
return sortedByName
}
return false
}
return sortedByNumComments
})
return topArticlesSlice
}
My structs (https://go.dev/play/p/27j-sFKaG2M)
type ArticleResponse struct {
Page int `json:"page"`
PerPage int `json:"per_page"`
Total int `json:"total"`
TotalPages int `json:"total_pages"`
Articles []*Article `json:"data"`
}
type Article struct {
Title *string `json:"title"`
URL *string `json:"url"`
Author string `json:"author"`
NumComments *int `json:"num_comments"`
StoryID interface{} `json:"story_id"`
StoryTitle *string `json:"story_title"`
StoryURL *string `json:"story_url"`
ParentID *int `json:"parent_id"`
CreatedAt int `json:"created_at"`
}
答案1
得分: 1
你的比较函数太复杂了。你需要将其重构为更简单、更直接的部分。
另外,你还没有定义你的Article
类型是什么样子的,所以为了举例,我将定义如下:
type Article struct {
NumComments *int
Title *string
}
你的基本要求是,首先按评论数量排序,然后(如果评论数量为nil)按标题的字母顺序排序,对吗?
从你的原始代码来看,
NumComments
是一个指向int的指针(*int
),Title
是一个指向字符串的指针(*string
)
这意味着每个比较都有四种情况需要处理:
X | Y | 动作 |
---|---|---|
非nil | 非nil | 比较x和y(根据它们的底层类型) |
非nil | nil | nil与非nil的比较结果是什么?(实现细节) |
nil | 非nil | nil与非nil的比较结果是什么?(实现细节) |
nil | nil | 两个nil在排序方面是相等的 |
为了完成这个练习,我将声明nil在与非nil的情况下排序靠后(但nil在与非nil的情况下排序靠前也是同样有效的。这是一种实现选择)。
比较两个*int
值很简单:
func compareIntPtr(x *int, y *int) int {
var cc int
switch {
case x != nil && y != nil:
cc = sign(*x - *y)
case x == nil && y == nil:
cc = 0
case x == nil && y != nil:
cc = +1
case x != nil && y == nil:
cc = -1
}
return cc
}
func sign(n int) int {
var sign int
switch {
case n < 0:
sign = -1
case n > 0:
sign = +1
default:
sign = 0
}
return sign
}
比较两个*string
值也是一样的:
import "strings"
.
.
.
func compareStringPtr(x *string, y *string) int {
var cc int
switch {
case x != nil && y != nil:
cc = strings.Compare(*x, *y)
case x == nil && y == nil:
cc = 0
case x == nil && y != nil:
cc = +1
case x != nil && y == nil:
cc = -1
}
return cc
}
一旦你有了这些基本操作,排序函数的比较器函数就更简单了:
func sortArticles(articles []*Article) []*Article {
topArticlesSlice := make([]*Article, 0)
topArticlesSlice = append(topArticlesSlice, articles[:]...)
sort.SliceStable(topArticlesSlice, func(i, j int) bool {
x := *topArticlesSlice[i]
y := *topArticlesSlice[j]
// 比较评论数量
cc := compareIntPtr(x.NumComments, y.NumComments)
// 如果相等,比较标题
if cc == 0 {
cc = compareStringPtr(x.Title, y.Title)
}
// 如果`x`排序小于`y`,返回`true`,否则返回`false`
return cc < 0
})
return topArticlesSlice
}
英文:
Your compare function is far too complex. You need to refactor it into simpler more straightforward bits.
And, you haven't defined what your Article
type looks like, so, for the purposes of example, I'm going to define it thus:
type Article struct {
NumComments *int
Title *string
}
Your basic ask is that you want to sort, first by the number of comments, and then (if the number of comments is nil) alphabetically by title, correct?
From your original code, it would appear that
NumComments
is a pointer to int (*int
), andTitle
is a pointer to string (*string
)
That means that each comparison has four cases that have to be dealt with:
X | Y | Action |
---|---|---|
non-nil | non-nil | Compare x and y (according to their underlying type) |
non-nil | nil | How does nil compare with non-nil? (implementation detail) |
nil | non-nil | How does nil compare with non-nil? (implementation detail) |
nil | nil | two nils compare equal for the purposes of collation |
For the purposes of this exercise, I'm going to declare that nil collates high with respect to non-nil (but nil collating low with respect to non-nil is equally valid. An implementation choice).
Comparing 2 *int
values is easy:
func compareIntPtr(x *int, y *int) int {
var cc int
switch {
case x != nil && y != nil: cc = sign(*x - *y)
case x == nil && y == nil: cc = 0
case x == nil && y != nil: cc = +1
case x != nil && y == nil: cc = -1
}
return cc
}
func sign(n int) int {
var sign int
switch {
case n < 0: sign = -1
case n > 0: sign = +1
default: sign = 0
}
return sign
}
As is comparing two *string
values:
import "strings"
.
.
.
func compareStringPtr(x *string, y *string) int {
var cc int
switch {
case x != nil && y != nil: cc = strings.Compare(*x, *y)
case x == nil && y == nil: cc = 0
case x == nil && y != nil: cc = +1
case x != nil && y == nil: cc = -1
}
return cc
}
Once you have those primitives, the comparer function for the sort is even simpler:
func sortArticles(articles []*Article) []*Article {
topArticlesSlice := make([]*Article, 0)
topArticlesSlice = append(topArticlesSlice, articles[:]...)
sort.SliceStable(topArticlesSlice, func(i, j int) bool {
x := *topArticlesSlice[i]
y := *topArticlesSlice[j]
// compare numbers of comments
cc := compareIntPtr(x.NumComments, y.NumComments)
// if equal, compare the titles
if cc == 0 {
cc = compareStringPtr(x.Title, y.Title)
}
// return `true` if `x` collates less than `y`, otherwise `false`
return cc < 0
})
return topArticlesSlice
}
答案2
得分: 0
以下是你提供的代码的翻译:
package main
import (
"fmt"
"sort"
)
type Article struct {
Title string
NumComments int
}
func main() {
a1 := Article{"狗", 3}
a2 := Article{"老虎", 0}
a3 := Article{"猫", 4}
a4 := Article{"鱼", 0}
a5 := Article{"鲸鱼", 8}
articles := []Article{a1, a2, a3, a4, a5}
sort.Slice(articles, func(i, j int) bool {
if articles[i].NumComments == 0 && articles[j].NumComments == 0 {
return articles[i].Title < articles[j].Title
} else {
return articles[i].NumComments < articles[j].NumComments
}
})
fmt.Printf("文章: %v\n", articles)
}
请注意,我在代码中将动物的名称翻译为中文。如果你有其他问题,请随时提问。
英文:
package main
import (
"fmt"
"sort"
)
type Article struct {
Title string
NumComments int
}
func main() {
a1 := Article{"Dog", 3}
a2 := Article{"Tiger", 0}
a3 := Article{"Cat", 4}
a4 := Article{"Fish", 0}
a5 := Article{"Whale", 8}
articles := []Article{a1, a2, a3, a4, a5}
sort.Slice(articles, func(i, j int) bool {
if articles[i].NumComments == 0 && articles[j].NumComments == 0 {
return articles[i].Title < articles[j].Title
} else {
return articles[i].NumComments < articles[j].NumComments
}
})
fmt.Printf("articles: %v\n", articles)
}
Some of the type definitions are missing in your post. I have taken a simple strut example. I think this is what you may be looking for?
答案3
得分: 0
假设我正确理解了你的要求,你可以使用类似以下的代码:
func sortArticles(articles []*Article) []*Article {
topArticlesSlice := append([]*Article{}, articles[:]...)
sort.SliceStable(topArticlesSlice, func(i, j int) bool {
if topArticlesSlice[i].NumComments != nil && topArticlesSlice[j].NumComments != nil &&
*topArticlesSlice[i].NumComments != *topArticlesSlice[j].NumComments {
return *topArticlesSlice[i].NumComments < *topArticlesSlice[j].NumComments
}
if topArticlesSlice[i].Title != nil && topArticlesSlice[j].Title != nil &&
*topArticlesSlice[i].Title != *topArticlesSlice[j].Title {
return *topArticlesSlice[i].Title < *topArticlesSlice[j].Title
} else if topArticlesSlice[i].StoryTitle != nil && topArticlesSlice[j].StoryTitle != nil {
return *topArticlesSlice[i].StoryTitle < *topArticlesSlice[j].StoryTitle
}
return false
})
return topArticlesSlice
}
你可以在playground中尝试这个代码。
英文:
Assuming I'm understanding your requirement correctly you can use something like the following:
func sortArticles(articles []*Article) []*Article {
topArticlesSlice := append([]*Article{}, articles[:]...)
sort.SliceStable(topArticlesSlice, func(i, j int) bool {
if topArticlesSlice[i].NumComments != nil && topArticlesSlice[j].NumComments != nil &&
*topArticlesSlice[i].NumComments != *topArticlesSlice[j].NumComments {
return *topArticlesSlice[i].NumComments < *topArticlesSlice[j].NumComments
}
if topArticlesSlice[i].Title != nil && topArticlesSlice[j].Title != nil &&
*topArticlesSlice[i].Title != *topArticlesSlice[j].Title {
return *topArticlesSlice[i].Title < *topArticlesSlice[j].Title
} else if topArticlesSlice[i].StoryTitle != nil && topArticlesSlice[j].StoryTitle != nil {
return *topArticlesSlice[i].StoryTitle < *topArticlesSlice[j].StoryTitle
}
return false
})
return topArticlesSlice
}
Try this in the playground.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论