【程式語言 - Go】關於Struct List的排序

更新於 發佈於
raw-image


排序這個動作在軟體開發中常常會使用到, 從使用者期望所見的順序到資料處理的效能議題都與排序息息相關, 因此掌握程式語言的排序功能是非常重要的一個環節, 而我們在閱讀他人的Go專案程式碼時也會看到排序的方式有些許不同, 那究竟有何差異呢? 就讓我們繼續看下去吧…

其實在進入今天的主題之前, 我們心中只要記得排序方式不同的主因主要有兩點不同, 「快速簡易」與「高度客製化」這兩個需求的差異之下衍生了不同的排序方式, 其實軟體寫久了大家會覺得程式語言與真實需求的許多場景息息相關, 畢竟需求是「人」衍生出來的, 而我們「人」所設計的程式語言也是圍繞著「需求」而生。

前置條件

假設我們定義了一個User的struct, 將儲存姓名與年齡。

type User struct {
Name string
Age int
}

那我們建立幾個user如下:

func main() {
// 創建包含 User 結構的切片
users := []User{
{"Alice", 30},
{"Bob", 25},
{"Charlie", 35},
}
}

Q: 我們今天收到一個需求, 請對於「年齡(Age)」進行升冪(由低到高)的排序, 請問應該怎麼做?

Slice函數排序

首先我們可以先看看Slice函數, 它提供了自訂比較函式的功能, 因此我們可以基於這樣的功能來設計我們想要怎麼排, 由於內建並沒有很直接的對struct資料結構進行排序的功能, 因此我們可以利用「sort.Slice」的特性來進行, 函式的使用大致如下:

// x 欲排序的結構目標
// less 自訂比較函式
func Slice(x any, less func(i, j int) bool) {
...
}

理解了使用方式之後我們就可以這樣排:

// 使用 sort.Slice 函數對切片進行排序
sort.Slice(users, func(i, j int) bool {
return users[i].Age < users[j].Age
})

接著印出結果:

// 打印排序後的切片
fmt.Println("按年齡排序:")
for _, user := range users {
fmt.Printf("Name: %s, Age: %d\\n", user.Name, user.Age)
}
raw-image


但這邊要特別注意的地方是官方的說明文件(https://pkg.go.dev/sort#Slice)中指出「這種排序不保證穩定性, 也就是兩個元素等值時, 每次執行後的結果不一定都相同」, 對於穩定的排序結果請使用「SliceStable」。

raw-image


// 使用 sort.Slice 函數對切片進行排序
sort.SliceStable(users, func(i, j int) bool {
return users[i].Age < users[j].Age
})

高度客製的自訂排序功能

我們都知道, 排序演算法裡面最重要的三個部份就是:

  • 長度多少?
  • 排序的規則?
  • 怎麼換?

以上三點我們可以將原本的struct進行三種功能的擴充, 而以下的程式碼是我們標準的排序方式。

type Users []User

// Len 方法返回切片的長度
func (u Users) Len() int {
return len(u)
}

// Less 方法定義了排序的規則,這裡按照 Age 升序排序
func (u Users) Less(i, j int) bool {
return u[i].Age < u[j].Age
}

// Swap 方法交換切片中兩個元素的位置
func (u Users) Swap(i, j int) {
u[i], u[j] = u[j], u[i]
}

設計完排序的長度、規則、換法之後, 實際上就可以很容易的進行Sort….

func main() {
...

// 使用 sort.Sort 函數來排序,使用自定義排序方式
sort.Sort(users)

fmt.Println("按年齡排序:")
for _, user := range users {
fmt.Printf("Name: %s, Age: %d\\n", user.Name, user.Age)
}
}

看到這邊您可能會疑惑, 為什麼要多此一舉, 不用前面所介紹的「sort.SliceStable」或者「sort.Slice」就好了呢? 因為我們總是看到事情的某個面位, 事實上面對於複雜的案例, 為了可讀性與不同的排序法則時我們就會需要這樣設計了。

假設你正在開發一個電子商務網站,你有一個商品結構體和一個購物車結構體,你希望能夠根據不同條件對購物車中的商品進行排序,例如按照價格、按照銷量、按照名稱等。

raw-image


package main

import (
"fmt"
"sort"
)

type Product struct {
Name string
Price float64
Quantity int
}

type ShoppingCart struct {
Items []Product
}

// 替購物車定義一個排序的介面
type Sorter interface {
Len() int
Swap(i, j int)
Less(i, j int) bool
}

// 設計一個按照價格排序的類別
type ByPrice ShoppingCart

func (s ByPrice) Len() int { return len(s.Items) }
func (s ByPrice) Swap(i, j int) { s.Items[i], s.Items[j] = s.Items[j], s.Items[i] }
func (s ByPrice) Less(i, j int) bool { return s.Items[i].Price < s.Items[j].Price }

// 設計一個按銷量排序的類別
type ByQuantity ShoppingCart

func (s ByQuantity) Len() int { return len(s.Items) }
func (s ByQuantity) Swap(i, j int) { s.Items[i], s.Items[j] = s.Items[j], s.Items[i] }
func (s ByQuantity) Less(i, j int) bool { return s.Items[i].Quantity < s.Items[j].Quantity }

func main() {
cart := ShoppingCart{
Items: []Product{
{"Laptop", 1000.0, 50},
{"Phone", 500.0, 100},
{"Tablet", 300.0, 30},
},
}

// 按照價格排序
sort.Sort(ByPrice(cart))
fmt.Println("按價格排序:", cart.Items)

// 按照銷量排序
sort.Sort(ByQuantity(cart))
fmt.Println("按銷量排序:", cart.Items)
}

在上面的範例中,我們為購物車定義了兩種不同的排序方式,以價格(ByPrice)和依照銷售(ByQuantity),這兩種排序方式都實作了Sorter介面的方法,然後,我們可以使用sort.Sort函數來對購物車中的商品進行排序。

結語

軟體開發的過程中常常會遇到的就是排序問題,基本上目前程式語言大部分都已經將排序功能封裝的非常易用了,我們不需要從頭到尾實作演算法,雖然如此,但我們還是得學會基本的原理以及這些API的使用方式,而每個語言的排序API又有些差異,但相信我們只要掌握核心就能夠得心應手。

留言
avatar-img
留言分享你的想法!
avatar-img
阿Han的沙龍
129會員
282內容數
哈囉,我是阿Han,是一位 👩‍💻 軟體研發工程師,喜歡閱讀、學習、撰寫文章及教學,擅長以圖代文,化繁為簡,除了幫助自己釐清思路之外,也希望藉由圖解的方式幫助大家共同學習,甚至手把手帶您設計出高品質的軟體產品。
你可能也想看
Thumbnail
大家好,我是一名眼科醫師,也是一位孩子的媽 身為眼科醫師的我,我知道視力發展對孩子來說有多關鍵。 每到開學季時,診間便充斥著許多憂心忡忡的家屬。近年來看診中,兒童提早近視、眼睛疲勞的案例明顯增加,除了3C使用過度,最常被忽略的,就是照明品質。 然而作為一位媽媽,孩子能在安全、舒適的環境
Thumbnail
大家好,我是一名眼科醫師,也是一位孩子的媽 身為眼科醫師的我,我知道視力發展對孩子來說有多關鍵。 每到開學季時,診間便充斥著許多憂心忡忡的家屬。近年來看診中,兒童提早近視、眼睛疲勞的案例明顯增加,除了3C使用過度,最常被忽略的,就是照明品質。 然而作為一位媽媽,孩子能在安全、舒適的環境
Thumbnail
我的「媽」呀! 母親節即將到來,vocus 邀請你寫下屬於你的「媽」故事——不管是紀錄爆笑的日常,或是一直想對她表達的感謝,又或者,是你這輩子最想聽她說出的一句話。 也歡迎你曬出合照,分享照片背後的點點滴滴 ♥️ 透過創作,將這份情感表達出來吧!🥹
Thumbnail
我的「媽」呀! 母親節即將到來,vocus 邀請你寫下屬於你的「媽」故事——不管是紀錄爆笑的日常,或是一直想對她表達的感謝,又或者,是你這輩子最想聽她說出的一句話。 也歡迎你曬出合照,分享照片背後的點點滴滴 ♥️ 透過創作,將這份情感表達出來吧!🥹
Thumbnail
列出一套完整的程式 程式設計有許多種方法,不過通常會先列出清單的再逐一執行,這樣會加快程式設計的速度。設計通常會採取順推的辦法。所以順推的程式設計方式就是經歷觀念溝通、系統分析、資料統合、權限管理、頻率與時間、後台管理、畫面設計等等階段後,將框架設計完了以後,先列出一套完整的程式,將所有使用者都確
Thumbnail
列出一套完整的程式 程式設計有許多種方法,不過通常會先列出清單的再逐一執行,這樣會加快程式設計的速度。設計通常會採取順推的辦法。所以順推的程式設計方式就是經歷觀念溝通、系統分析、資料統合、權限管理、頻率與時間、後台管理、畫面設計等等階段後,將框架設計完了以後,先列出一套完整的程式,將所有使用者都確
Thumbnail
排序這個動作在軟體開發中常常會使用到, 從使用者期望所見的順序到資料處理的效能議題都與排序息息相關, 因此掌握程式語言的排序功能是非常重要的一個環節, 而我們在閱讀他人的Go專案程式碼時也會看到排序的方式有些許不同, 那究竟有何差異呢? 就讓我們繼續看下去吧… 其實在進入今天的主題之前, 我們
Thumbnail
排序這個動作在軟體開發中常常會使用到, 從使用者期望所見的順序到資料處理的效能議題都與排序息息相關, 因此掌握程式語言的排序功能是非常重要的一個環節, 而我們在閱讀他人的Go專案程式碼時也會看到排序的方式有些許不同, 那究竟有何差異呢? 就讓我們繼續看下去吧… 其實在進入今天的主題之前, 我們
Thumbnail
👨‍💻簡介 套件(Package)在Golang中扮演著組織和管理程式碼的重要角色。 package就像工具箱一樣,裡面裝滿各種不同的工具,每個工具都有特定的功能。這些工具能夠幫助你完成不同的任務,從修理家具到蓋小屋,樣樣都行。
Thumbnail
👨‍💻簡介 套件(Package)在Golang中扮演著組織和管理程式碼的重要角色。 package就像工具箱一樣,裡面裝滿各種不同的工具,每個工具都有特定的功能。這些工具能夠幫助你完成不同的任務,從修理家具到蓋小屋,樣樣都行。
Thumbnail
👨‍💻簡介 在程式開發的世界中,我們經常需要處理各式各樣的資料,可能是一個人的個人資訊,也可能是一個商品的詳細訊息。當我們面對這麼多的資料時,如何將它們有系統地整理起來,讓我們能夠輕鬆地找到所需,便成了一個重要的課題。這時,結構體的概念就像是一道曙光,為我們提供了一個非常有力的工具。 結
Thumbnail
👨‍💻簡介 在程式開發的世界中,我們經常需要處理各式各樣的資料,可能是一個人的個人資訊,也可能是一個商品的詳細訊息。當我們面對這麼多的資料時,如何將它們有系統地整理起來,讓我們能夠輕鬆地找到所需,便成了一個重要的課題。這時,結構體的概念就像是一道曙光,為我們提供了一個非常有力的工具。 結
Thumbnail
👨‍💻簡介 在 Go 語言中,函數(Function)是一個強大且重要的概念,就像食譜一樣,告訴你應該如何處理食材,最後得到一道美味的料理。經過哪些程序讓程式更有組織性和可讀性。函數可幫助你將程式碼區塊組織成可重複使用的元件,進而執行特定的任務。
Thumbnail
👨‍💻簡介 在 Go 語言中,函數(Function)是一個強大且重要的概念,就像食譜一樣,告訴你應該如何處理食材,最後得到一道美味的料理。經過哪些程序讓程式更有組織性和可讀性。函數可幫助你將程式碼區塊組織成可重複使用的元件,進而執行特定的任務。
Thumbnail
大家好,歡迎來到「Golang - Design Pattern」系列文!你或許已經聽過「設計模式」這四個字,或者你可能還不太清楚它在軟體開發中的角色。不管你是哪一種情況,我們都會從頭開始,循序漸進地為你解鎖Go語言在設計模式中的神奇之處。 簡介 設計模式是什麼呢?簡單地說,它就是一套「最佳
Thumbnail
大家好,歡迎來到「Golang - Design Pattern」系列文!你或許已經聽過「設計模式」這四個字,或者你可能還不太清楚它在軟體開發中的角色。不管你是哪一種情況,我們都會從頭開始,循序漸進地為你解鎖Go語言在設計模式中的神奇之處。 簡介 設計模式是什麼呢?簡單地說,它就是一套「最佳
Thumbnail
👨‍💻簡介 在這篇文章裡,會簡單介紹幾個關鍵的基本概念和語法結構,加快上手這門程式語言。
Thumbnail
👨‍💻簡介 在這篇文章裡,會簡單介紹幾個關鍵的基本概念和語法結構,加快上手這門程式語言。
Thumbnail
Golang(Go)是由Google開發的一種開源的、靜態型別的編程語言,目的在提供一個簡單、高效、安全以及易於擴展的程式語言,特別適用於並發和平行處理,Go語言設計上著重於開發者的生產力,並提供了現代化的語法和豐富的標準庫。 強調簡單易讀,讓我們回歸原始以最簡易的方式來撰寫程式,併發的特性讓日趨
Thumbnail
Golang(Go)是由Google開發的一種開源的、靜態型別的編程語言,目的在提供一個簡單、高效、安全以及易於擴展的程式語言,特別適用於並發和平行處理,Go語言設計上著重於開發者的生產力,並提供了現代化的語法和豐富的標準庫。 強調簡單易讀,讓我們回歸原始以最簡易的方式來撰寫程式,併發的特性讓日趨
Thumbnail
這邊要來跟大家介紹C#,使用的軟體會是Visual Studio 2017版本 介紹內容架構可以分六大階段:入門級、基礎級、中低階級、中階級、中高階級、高階級 在正式開始寫程式前,要先了解一些基本概念哦! 就如同學中文前要先了解ㄅㄆㄇㄈ或羅馬拼音之類的,程式也是一樣道理,先把基本概念了解後再來開始
Thumbnail
這邊要來跟大家介紹C#,使用的軟體會是Visual Studio 2017版本 介紹內容架構可以分六大階段:入門級、基礎級、中低階級、中階級、中高階級、高階級 在正式開始寫程式前,要先了解一些基本概念哦! 就如同學中文前要先了解ㄅㄆㄇㄈ或羅馬拼音之類的,程式也是一樣道理,先把基本概念了解後再來開始
Thumbnail
C# List – (C#教學) – 所謂List, 就是把多個同類的variable串列在一起. 加上for, foreach迴圈的使用, 令他們可以作出相同的行為. 相關頁面: C# 陣列 – 學會Array陣列基本5種應用方法 – 初始化, 加入值, 更新值, 刪除值, foreach迴圈
Thumbnail
C# List – (C#教學) – 所謂List, 就是把多個同類的variable串列在一起. 加上for, foreach迴圈的使用, 令他們可以作出相同的行為. 相關頁面: C# 陣列 – 學會Array陣列基本5種應用方法 – 初始化, 加入值, 更新值, 刪除值, foreach迴圈
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News