在 Go 中组合不同的排序

发布于 2025-01-11 18:21:51 字数 2206 浏览 0 评论 0原文

如何在 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"`
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

少女的英雄梦 2025-01-18 18:21:51

您的比较功能太复杂了。您需要将其重构为更简单、更直接的部分。

而且,您还没有定义您的 Article 类型,因此,出于示例的目的,我将这样定义它:

type Article struct {
    NumComments *int
    Title       *string
}

您的基本要求是您想要排序,首先按评论数,然后(如果评论数为零)按标题字母顺序排列,对吗?

从您的原始代码来看,

  • NumComments 是一个指向 int (*int) 的指针,
  • Title 是一个指向字符串 (< code>*string)

这意味着每次比较都有四种种情况需要处理:

XYAction
non-nilnon-nil比较 x 和 y(根据它们的基础类型)
非零 nilnil是如何实现的与非零比较? (实现细节)
nilnon-nilnil 与 non-nil 相比如何? (实现细节)
nilnil两个 nil 出于整理目的比较相等

出于本练习的目的,我将声明 nil 相对于非 nil 进行高整理(但 nil 相对于非 nil 进行低整理是同样有效的实施选择)。

比较 2 个 *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]
    
    // 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
}

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), and
  • Title is a pointer to string (*string)

That means that each comparison has four cases that have to be dealt with:

XYAction
non-nilnon-nilCompare x and y (according to their underlying type)
non-nilnilHow does nil compare with non-nil? (implementation detail)
nilnon-nilHow does nil compare with non-nil? (implementation detail)
nilniltwo 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
}
只是一片海 2025-01-18 18:21:51
    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)
}

您的帖子中缺少一些类型定义。我举了一个简单的支柱示例。我想这就是您可能正在寻找的?

    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?

骷髅 2025-01-18 18:21:51

假设我正确理解您的要求,您可以使用类似以下内容:


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
}

游乐场

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文