Golang 高效處理集合 -Collection- 的庫

處理集合是構建任何應用程序的重要部分。通常,需要以下幾類操作:

儘管 Go 具有許多優點,但對於高級集合操作的內置支持相對有限,因此如果需要,需要使用第三方包。在本文中,我將探討幾個流行的 Go 庫,這些庫可以增強語言的能力,以有效地處理集合,涵蓋它們的功能和功能。這篇評論將幫助開發者選擇合適的工具,以簡化 Go 項目中的數據處理任務。

Introduction

讓我們從上面的每個集合操作類中回顧一些流行的方法。

轉換

Map — 對集合中的每個元素應用一個函數,並返回結果集合;FlatMap — 將每個元素處理爲一個元素列表,然後將這些列表展平爲一個列表。

過濾

Filter — 刪除不匹配謂詞函數的元素;Distinct — 從集合中刪除重複的元素;TakeWhile — 返回滿足給定條件的元素,直到遇到不滿足條件的元素爲止;DropWhile — 刪除滿足給定條件的元素,然後返回剩餘的元素。

聚合

Reduce — 使用給定的函數組合集合的所有元素,並返回組合結果;Count — 返回滿足特定條件的元素數量;Sum — 計算集合中每個元素的數字屬性之和;Max/Min — 確定元素屬性中的最大值或最小值;Average — 計算集合中元素的數字屬性的平均值。

排序 / 排序

Sort — 根據比較器規則對集合的元素進行排序;Reverse — 顛倒集合中元素的順序。

訪問

Find — 返回匹配給定謂詞的第一個元素;AtIndex — 檢索特定索引處的元素。

實用程序

GroupBy — 根據鍵生成器函數將元素分類爲組;Partition — 根據謂詞將集合分成兩個集合:一個用於滿足謂詞的元素,另一個用於不滿足謂詞的元素;Slice Operations — 修改集合視圖或劃分的操作,如切片或分塊。

Go 內置的能力

在 Go 語言中,有幾種類型可用於處理數據集合:

數組(Arrays) — 固定大小的元素集合。數組大小在聲明時定義,例如 var myArray [5]int;切片(Slices) — 動態大小的元素集合。切片建立在數組之上,但與數組不同的是,它們可以增長或縮小。聲明方式:mySlice := []int{1, 2, 3};映射(Maps) — 鍵 - 值對的集合。映射可以動態增長,且鍵的順序不受保證。例如 myMap := map[string]int{"first": 1, "second": 2} 創建了一個字符串鍵和整數值的映射;通道(Channels) — 類型化的通信原語,允許在 goroutine 之間共享數據。例如 myChan := make(chan int) 創建了一個傳輸整數的通道。

Go 標準庫提供了其他結構和實用程序,可以作爲集合或增強集合的功能,例如:

堆(Heap) — container/heap 包爲任何 sort.Interface 提供了堆操作。堆是具有以下特性的樹:每個節點都是其子樹中值最小的節點;鏈表(List) — container/list 包實現了雙向鏈表;環形鏈表(Ring) — container/ring 包實現了環形鏈表的操作。

此外,作爲 Go 標準庫的一部分,還有用於處理切片和映射的包:

slices — 該包定義了與任何類型的切片一起使用的各種有用的函數;maps — 該包定義了與任何類型的映射一起使用的各種有用的函數。

通過內置功能,我可以對集合執行一些操作:

獲取數組 / 切片 / 映射的長度;通過索引 / 鍵訪問元素,對切片進行 “切片”;遍歷項目。

package main

import "fmt"

func main() {
 s := []int{1, 2, 3, 4, 5}
 m := map[int]string{1: "one", 2: "two", 3: "three"}

 fmt.Printf("len(s)=%d\n", len(s))
 fmt.Printf("len(m)=%d\n", len(m))
 fmt.Printf("cap(s)=%d\n", cap(s))
 // fmt.Printf("cap(m)=%d\n", cap(m)) // error: invalid argument m (type map[int]string) for cap

 // panic: runtime error: index out of range [5] with length 5
 // fmt.Printf("s[5]=%d\n", s[5])

 // panic: runtime error: index out of range [5] with length 5
 // s[5] = 6

 s = append(s, 6)
 fmt.Printf("s=%v\n", s)
 fmt.Printf("len(s)=%d\n", len(s))
 fmt.Printf("cap(s)=%d\n", cap(s))

 m[4] = "four"
 fmt.Printf("m=%v\n", m)

 fmt.Printf("s[2:4]=%v\n", s[2:4])
 fmt.Printf("s[2:]=%v\n", s[2:])
 fmt.Printf("s[:2]=%v\n", s[:2])
 fmt.Printf("s[:]=%v\n", s[:])
}

上面的代碼會打印:

len(s)=5
len(m)=3
cap(s)=5
s=[1 2 3 4 5 6]
len(s)=6
cap(s)=10
m=map[1:one 2:two 3:three 4:four]
s[2:4]=[3 4]
s[2:]=[3 4 5 6]
s[:2]=[1 2]
s[:]=[1 2 3 4 5 6]

讓我們逐個看下 Go 內置的 Package。

Slices

切片 slices 包最近纔出現在 Go 標準庫中,從 Go 1.21 版本開始。這是語言中的一個重大進步,但我仍然更喜歡使用外部庫來處理集合。讓我們來看看這個庫如何支持所有的集合操作類別。

Aggregation

slices 能夠快速在切片中找到最小 / 最大值:

package main

import (
 "fmt"
 "slices"
)

type Example struct {
 Name   string
 Number int
}

func main() {
 s := []int{1, 2, 3, 4, 5}

 fmt.Printf("Min: %d\n", slices.Min(s))
 fmt.Printf("Max: %d\n", slices.Max(s))

 e := []Example{
  {"A", 1},
  {"B", 2},
  {"C", 3},
  {"D", 4},
 }

 fmt.Printf("Min: %v\n", slices.MinFunc(
  e,
  func(i, j Example) int {
   return i.Number - j.Number
  }),
 )

 fmt.Printf("Max: %v\n", slices.MaxFunc(
  e,
  func(i, j Example) int {
   return i.Number - j.Number
  }),
 )
}

上面的代碼打印:

Min: 1
Max: 5
Min: {A 1}
Max: {D 4}

Sorting/Ordering

slices 能夠使用比較函數對切片進行排序:

package main

import (
 "fmt"
 "slices"
)

type Example struct {
 Name   string
 Number int
}

func main() {
 s := []int{4, 2, 5, 1, 3}

 slices.Sort(s)
 fmt.Printf("Sorted: %v\n", s)

 slices.Reverse(s)
 fmt.Printf("Reversed: %v\n", s)

 e := []Example{
  {"C", 3},
  {"A", 1},
  {"D", 4},
  {"B", 2},
 }

 slices.SortFunc(e, func(a, b Example) int {
  return a.Number - b.Number
 })

 fmt.Printf("Sorted: %v\n", e)

 slices.Reverse(e)
 fmt.Printf("Reversed: %v\n", e)
}
Sorted: [1 2 3 4 5]
Reversed: [5 4 3 2 1]
Sorted: [{A 1} {B 2} {C 3} {D 4}]
Reversed: [{D 4} {C 3} {B 2} {A 1}]

不過這個方法有個缺點,就是排序是原地進行的,修改了原始切片。如果該方法返回一個新的排序後的切片,從而保留原始數組會更好一點。

訪問元素

slices暴露了一些方法,允許用戶在切片中查找元素的位置:

package main

import (
 "fmt"
 "slices"
)

type Example struct {
 Name   string
 Number int
}

func main() {
 s := []int{4, 2, 5, 1, 3}

 i := slices.Index(s, 3)
 fmt.Printf("Index of 3: %d\n", i)

 e := []Example{
  {"C", 3},
  {"A", 1},
  {"D", 4},
  {"B", 2},
 }

 i = slices.IndexFunc(e, func(a Example) bool {
  return a.Number == 3
 })

 fmt.Printf("Index of 3: %d\n", i)
}
Index of 3: 4
Index of 3: 0

如果你正在處理已排序的切片,你可以使用 BinarySearch 或 BinarySearchFunc 在排序的切片中搜索目標,並返回目標被找到的位置或目標將出現在排序順序中的位置;它還返回一個布爾值,指示目標是否在切片中被找到。切片必須按遞增順序排序。

package main

import (
 "fmt"
 "slices"
)

func main() {
 s := []int{4, 2, 5, 1, 3}

 slices.Sort(s)

 i, found := slices.BinarySearch(s, 3)
 fmt.Printf("Position of 3: %d. Found: %t\n", i, found)

 i, found = slices.BinarySearch(s, 6)
 fmt.Printf("Position of 6: %d. Found: %t\n", i, found)
}
Position of 3: 2. Found: true
Position of 6: 5. Found: false

實用函數

slices 提供了許多實用函數:

package main

import (
 "fmt"
 "slices"
)

type Example struct {
 Name   string
 Number int
}

func main() {
 e1 := []Example{
  {"C", 3},
  {"A", 1},
  {"D", 4},
  {"B", 2},
 }

 e2 := []Example{
  {"A", 1},
  {"B", 2},
  {"C", 3},
  {"D", 4},
 }

 fmt.Printf("Compare: %v\n", slices.CompareFunc(e1, e2, func(a, b Example) int {
  return a.Number - b.Number
 }))

 fmt.Printf("Contains: %v\n", slices.ContainsFunc(e1, func(a Example) bool {
  return a.Number == 2
 }))

 fmt.Printf("Delete: %v\n", slices.Delete(e1, 2, 3))
 fmt.Printf("Equal: %v\n", slices.Equal(e1, e2))

 fmt.Printf("Is Sorted: %v\n", slices.IsSortedFunc(e1, func(a, b Example) int {
  return a.Number - b.Number
 }))
}
Compare: 2
Contains: true
Delete: [{C 3} {A 1} {B 2}]
Equal: false
Is Sorted: false

slices 包的官方文檔地址 [1]

Map

類似於 slices,maps 也是從 Go 1.21 開始出現在 Go 標準庫中的。它定義了各種方法來操作 maps。

package main

import (
 "fmt"
 "maps"
)

func main() {
 m := map[int]string{1: "one", 2: "two", 3: "three"}
 c := maps.Clone(m)

 c[4] = "four"

 fmt.Printf("Original: %v\n", m)
 fmt.Printf("Clone: %v\n", c)

 maps.DeleteFunc(c, func(k int, v string) bool { return k%2 == 0 })
 fmt.Printf("DeleteFunc: %v\n", c)

 fmt.Printf("Equal: %v\n", maps.Equal(m, c))
 fmt.Printf("EqualFunc: %v\n", maps.EqualFunc(m, c, func(v1, v2 string) bool { return v1 == v2 }))
}
Original: map[1:one 2:two 3:three]
Clone: map[1:one 2:two 3:three 4:four]
DeleteFunc: map[1:one 3:three]
Equal: false
EqualFunc: false

maps 包的官方文檔地址 [2]

github.com/elliotchance/pie

這是我個人最喜歡的用來操作切片和映射的包。它提供了一種獨特的語法,能夠無縫地鏈接操作,提高了代碼的可讀性和效率。

使用庫方法有四種方式:

  1. 純調用 — 只需調用庫方法並提供所需的參數;

  2. pie.Of — 鏈接多個操作,支持任何元素類型;

  3. pie.OfOrdered — 鏈接多個操作,支持數字和字符串類型;

  4. pie.OfNumeric — 鏈接多個操作,僅支持數字類型。

package main

import (
 "fmt"
 "strings"

 "github.com/elliotchance/pie/v2"
)

type Example struct {
 Name   string
 Number int
}

func main() {
 e := []Example{
  {"C", 3},
  {"A", 1},
  {"D", 4},
  {"B", 2},
 }

 fmt.Printf(
  "Map 1: %v\n",
  pie.Sort(
   pie.Map(
    e,
    func(e Example) string {
     return e.Name
    },
   ),
  ),
 )

 fmt.Printf(
  "Map 2: %v\n",
  pie.Of(e).
   Map(func(e Example) Example {
    return Example{
     Name:   e.Name,
     Number: e.Number * 2,
    }
   }).
   SortUsing(func(a, b Example) bool {
    return a.Number < b.Number
   }),
 )

 fmt.Printf(
  "Map 3: %v\n",
  pie.OfOrdered([]string{"A", "C", "B", "A"}).
   Map(func(e string) string {
    return strings.ToLower(e)
   }).
   Sort(),
 )

 fmt.Printf(
  "Map 4: %v\n",
  pie.OfNumeric([]int{4, 1, 3, 2}).
   Map(func(e int) int {
    return e * 2
   }).
   Sort(),
 )
}
Map 1: [A B C D]
Map 2: {[{A 2} {B 4} {C 6} {D 8}]}
Map 3: {[a a b c]}
Map 4: {[2 4 6 8]}

由於諸如 Map 等函數應該返回相同類型的集合,因此這個庫的鏈式操作相當受限。因此,我認爲純方法調用是使用這個庫的最佳方式。

該庫提供了 Map 方法,允許將每個元素從一種類型轉換爲另一種類型。

package main

import (
 "fmt"

 "github.com/elliotchance/pie/v2"
)

type Example struct {
 Name   string
 Number int
}

func main() {
 e := []Example{
  {"C", 3},
  {"A", 1},
  {"D", 4},
  {"B", 2},
 }

 fmt.Printf(
  "Map: %v\n",
  pie.Map(
   e,
   func(e Example) string {
    return e.Name
   },
  ),
 )
}

還提供了 Flat 方法,它將二維切片轉換爲一維切片。

package main

import (
 "fmt"

 "github.com/elliotchance/pie/v2"
)

type Person struct {
 Name string
 Tags []string
}

func main() {
 p := []Person{
  {"Alice", []string{"a", "b", "c"}},
  {"Bob", []string{"b", "c", "d"}},
  {"Charlie", []string{"c", "d", "e"}},
 }

 fmt.Printf(
  "Unique Tags: %v\n",
  pie.Unique(
   pie.Flat(
    pie.Map(
     p,
     func(e Person) []string {
      return e.Tags
     },
    ),
   ),
  ),
 )
}
Unique Tags: [b c d e a]

使用 Keys 或 Values 方法可以僅獲取 Map 的鍵或值。

package main

import (
 "fmt"

 "github.com/elliotchance/pie/v2"
)

func main() {
 m := map[int]string{
  1: "one",
  2: "two",
  3: "three",
 }

 fmt.Printf("Keys: %v\n", pie.Keys(m))
 fmt.Printf("Values: %v\n", pie.Values(m))
}

Output:

Keys: [3 1 2]
Values: [one two three]

Filter

該庫提供了幾種過濾原始集合的方法:Bottom、DropTop、DropWhile、Filter、FilterNot、Unique 等。

Bottom 3: [4 4 4]
Drop top 3: [3 3 3 4 4 4 4]
Drop while 3: [3 3 3 4 4 4 4]
Filter even: [2 2 4 4 4 4]
Filter not even: [1 3 3 3]
Unique values: [1 2 3 4]

Aggregation

有一個通用的聚合方法 Reduce。讓我們來計算標準差:

package main

import (
 "fmt"
 "math"

 "github.com/elliotchance/pie/v2"
)

func main() {
 v := []float64{1.1, 2.2, 3.3, 4.4, 5.5}

 avg := pie.Average(v)
 count := len(v)

 sum2 := pie.Reduce(
  v,
  func(acc, value float64) float64 {
   return acc + (value-avg)*(value-avg)
  },
 ) - v[0] + (v[0]-avg)*(v[0]-avg)

 d := math.Sqrt(sum2 / float64(count))

 fmt.Printf("Standard deviation: %f\n", d)
}

Output:

Standard deviation: 1.555635

Reduce 方法首先將第一個切片元素作爲累積值,將第二個元素作爲值參數調用 reducer。這就是爲什麼公式看起來很奇怪。

從下面的示例中,可以找到另一個內置的聚合方法 Average。此外,還可以找到 Min、Max、Product 等方法。

package main

import (
 "fmt"

 "github.com/elliotchance/pie/v2"
)

func main() {
 v := []float64{1.1, 2.2, 3.3, 4.4, 5.5}

 fmt.Printf("Average: %f\n", pie.Average(v))
 fmt.Printf("Stddev: %f\n", pie.Stddev(v))
 fmt.Printf("Max: %f\n", pie.Max(v))
 fmt.Printf("Min: %f\n", pie.Min(v))
 fmt.Printf("Sum: %f\n", pie.Sum(v))
 fmt.Printf("Product: %f\n", pie.Product(v))

 fmt.Printf("All >0: %t\n", pie.Of(v).All(func(value float64) bool { return value > 0 }))
 fmt.Printf("Any >5: %t\n", pie.Of(v).Any(func(value float64) bool { return value > 5 }))

 fmt.Printf("First: %f\n", pie.First(v))
 fmt.Printf("Last: %f\n", pie.Last(v))

 fmt.Printf("Are Unique: %t\n", pie.AreUnique(v))
 fmt.Printf("Are Sorted: %t\n", pie.AreSorted(v))
 fmt.Printf("Contains 3.3: %t\n", pie.Contains(v, 3.3))
}
Average: 3.300000
Stddev: 1.555635
Max: 5.500000
Min: 1.100000
Sum: 16.500000
Product: 193.261200
All >0: true
Any >5: true
First: 1.100000
Last: 5.500000
Are Unique: true
Are Sorted: true
Contains 3.3: true

Sorting/Ordering

有三種不同的方法可以使用 pie 對切片進行排序:

Sort — 類似於 sort.Slice。但與 sort.Slice 不同的是,返回的切片將被重新分配,以不修改輸入切片;SortStableUsing — 類似於 sort.SliceStable。但與 sort.SliceStable 不同的是,返回的切片將被重新分配,以不修改輸入切片;SortUsing — 類似於 sort.Slice。但與 sort.Slice 不同的是,返回的切片將被重新分配,以不修改輸入切片。

package main

import (
 "fmt"

 "github.com/elliotchance/pie/v2"
)

func main() {
 v := []int{3, 5, 1, 4, 2}

 less := func(a, b int) bool {
  return a < b
 }

 fmt.Printf("Sort: %v\n", pie.Sort(v))
 fmt.Printf("SortStableUsing: %v\n", pie.SortStableUsing(v, less))
 fmt.Printf("SortUsing: %v\n", pie.SortUsing(v, less))
 fmt.Printf("Original: %v\n", v)
}

Output:

Sort: [1 2 3 4 5]
SortStableUsing: [1 2 3 4 5]
SortUsing: [1 2 3 4 5]
Original: [3 5 1 4 2]

Access

pie 提供了 FindFirstUsing 方法,用於獲取切片中第一個與謂詞匹配的元素的索引。

package main

import (
 "fmt"

 "github.com/elliotchance/pie/v2"
)

type Person struct {
 Name string
 Age  int
}

func main() {
 p := []Person{
  {"Alice", 25},
  {"Bob", 30},
  {"Charlie", 35},
 }

 fmt.Printf(
  "FindFirstUsing: %v\n",
  pie.FindFirstUsing(
   p,
   func(p Person) bool {
    return p.Age >= 30
   },
  ),
 )

}
FindFirstUsing: 2

pie 包含許多用於處理切片的實用方法。舉幾個例子:

package main

import (
 "fmt"
 "math/rand"
 "time"

 "github.com/elliotchance/pie/v2"
)

type Person struct {
 Name string
 Age  int
}

func main() {
 p := []Person{
  {"Alice", 25},
  {"Bob", 30},
  {"Charlie", 35},
  {"David", 25},
  {"Eve", 40},
  {"Frank", 35},
 }

 fmt.Printf("Chunk: %v\n", pie.Chunk(p, 2))
 fmt.Printf("GroupBy: %v\n", pie.GroupBy(p, func(p Person) int { return p.Age }))
 fmt.Printf("Shuffle: %v\n", pie.Shuffle(p, rand.New(rand.NewSource(time.Now().UnixNano()))))
}

Output:

Chunk: [[{Alice 25} {Bob 30}] [{Charlie 35} {David 25}] [{Eve 40} {Frank 35}]]
GroupBy: map[25:[{Alice 25} {David 25}] 30:[{Bob 30}] 35:[{Charlie 35} {Frank 35}] 40:[{Eve 40}]]
Shuffle: [{Frank 35} {Bob 30} {David 25} {Eve 40} {Alice 25} {Charlie 35}]

下面是 pie 包的地址 [3]

elliotchance/pie/v2 庫提供了一套非常完整的的處理集合的能力,極大地簡化了在 Go 中處理切片的工作。其強大的方法用於操作和查詢切片數據,爲開發人員提供了一個強大的工具,增強了代碼的可讀性和效率。我強烈建議任何 Go 開發人員在下一個項目中嘗試使用這個庫。

github.com/samber/lo

另一個在 Go 中操作集合的流行庫。在某些方面,它可能類似於流行的 JavaScript 庫 Lodash。它在內部使用泛型,而不是反射。

Transform

該庫支持默認的 MapFlatMap 方法用於切片:

package main

import (
 "fmt"

 "github.com/samber/lo"
)

type Example struct {
 Name   string
 Number int
}

func main() {
 e := []Example{
  {"C", 3},
  {"A", 1},
  {"D", 4},
  {"B", 2},
 }

 fmt.Printf(
  "Map: %v\n",
  lo.Map(
   e,
   func(e Example, index int) string {
    return e.Name
   },
  ),
 )
}
Map: [C A D B]

下面的代碼演示如何操作 FlatMap:

package main

import (
 "fmt"

 "github.com/samber/lo"
)

type Person struct {
 Name string
 Tags []string
}

func main() {
 p := []Person{
  {"Alice", []string{"a", "b", "c"}},
  {"Bob", []string{"b", "c", "d"}},
  {"Charlie", []string{"c", "d", "e"}},
 }

 fmt.Printf(
  "Unique Tags: %v\n",
  lo.Uniq(
   lo.FlatMap(
    p,
    func(e Person, index int) []string {
     return e.Tags
    },
   ),
  ),
 )
}

此外,還可以獲取映射鍵、值或將映射對轉換爲某些切片等操作:

package main

import (
 "fmt"
 "strings"

 "github.com/samber/lo"
)

func main() {
 m := map[int]string{
  1: "one",
  2: "two",
  3: "three",
 }

 fmt.Printf("Keys: %v\n", lo.Keys(m))
 fmt.Printf("Values: %v\n", lo.Values(m))
 fmt.Printf("MapKeys: %v\n", lo.MapKeys(m, func(value string, num int) int { return num * 2 }))
 fmt.Printf("MapValues: %v\n", lo.MapValues(m, func(value string, num int) string { return strings.ToUpper(value) }))
 fmt.Printf("MapToSlice: %v\n", lo.MapToSlice(m, func(num int, value string) string { return value + ":" + fmt.Sprint(num) }))
}

Outputs:

Keys: [2 3 1]
Values: [one two three]
MapKeys: map[2:one 4:two 6:three]
MapValues: map[1:ONE 2:TWO 3:THREE]
MapToSlice: [three:3 one:1 two:2]

Filter

在 lo 庫中有許多 Drop 方法:

package main

import (
 "fmt"

 "github.com/samber/lo"
)

func main() {
 v := []int{1, 2, 3, 4, 5}

 fmt.Printf("Drop: %v\n", lo.Drop(v, 2))
 fmt.Printf("DropRight: %v\n", lo.DropRight(v, 2))
 fmt.Printf("DropWhile: %v\n", lo.DropWhile(v, func(i int) bool { return i < 3 }))
 fmt.Printf("DropRightWhile: %v\n", lo.DropRightWhile(v, func(i int) bool { return i > 3 }))
}

Outputs:

Drop: [3 4 5]
DropRight: [1 2 3]
DropWhile: [3 4 5]
DropRightWhile: [1 2 3]

此外,還可以通過 predicate 過濾切片和映射:

package main

import (
 "fmt"

 "github.com/samber/lo"
)

func main() {
 v := []int{1, 2, 3, 4, 5}
 m := map[string]int{"a": 1, "b": 2, "c": 3}

 fmt.Printf("Filter: %v\n", lo.Filter(v, func(i int, index int) bool { return i > 2 }))
 fmt.Printf("PickBy: %v\n", lo.PickBy(m, func(key string, value int) bool { return value > 2 }))
}

Outputs:

Filter: [3 4 5]
PickBy: map[c:3]

Aggregation

lo package 提供了 reduce 的方法:

package main

import (
 "fmt"
 "math"

 "github.com/samber/lo"
)

func main() {
 v := []float64{1.1, 2.2, 3.3, 4.4, 5.5}

 count := len(v)

 avg := lo.Reduce(v, func(acc, val float64, index int) float64 {
  return acc + val
 }, 0.0) / float64(count)

 sum2 := lo.Reduce(v, func(acc, val float64, index int) float64 {
  return acc + (val-avg)*(val-avg)
 }, 0.0)

 d := math.Sqrt(sum2 / float64(count))

 fmt.Printf("Standard deviation: %f\n", d)
}

Outputs:

Standard deviation: 1.555635

此外,它支持一些通用的聚合方法,如 Sum、Min、Max:

package main

import (
 "fmt"

 "github.com/samber/lo"
)

func main() {
 v := []float64{1.1, 2.2, 3.3, 4.4, 5.5}

 fmt.Printf("Sum: %v\n", lo.Sum(v))
 fmt.Printf("Min: %v\n", lo.Min(v))
 fmt.Printf("Max: %v\n", lo.Max(v))
}

有一些有用的方法用於處理 channel:FanIn 和 FanOut

package main

import (
 "fmt"

 "github.com/samber/lo"
)

func main() {
 ch1 := make(chan int)
 ch2 := make(chan int)
 ch3 := make(chan int)

 ch := lo.FanIn(10, ch1, ch2, ch3)

 for i := 0; i < 10; i++ {
  if i%3 == 0 {
   ch1 <- i
  } else if i%3 == 1 {
   ch2 <- i
  } else {
   ch3 <- i
  }
 }

 close(ch1)
 close(ch2)
 close(ch3)

 for v := range ch {
  fmt.Println(v)
 }
}

還可以這樣處理 channel:

package main

import (
 "fmt"

 "github.com/samber/lo"
)

func main() {
 ch := make(chan int)
 chs := lo.FanOut(3, 10, ch)

 for i := 0; i < 3; i++ {
  ch <- i
 }

 close(ch)

 for _, ch := range chs {
  for v := range ch {
   fmt.Println(v)
  }
 }
}

Sorting/Ordering

lo 還提供 Reverse 方法:

package main

import (
 "fmt"

 "github.com/samber/lo"
)

func main() {
 v := []int{1, 2, 3, 4, 5}

 fmt.Printf("Reverse: %v\n", lo.Reverse(v))
}

Access

lo 庫提供了 find 方法來訪問元素:

package main

import (
 "fmt"

 "github.com/samber/lo"
)

type Person struct {
 Name string
 Age  int
}

func main() {
 p := []Person{
  {"Alice", 25},
  {"Bob", 30},
  {"Charlie", 35},
  {"David", 25},
  {"Edward", 40},
 }

 item, found := lo.Find(p, func(p Person) bool {
  return p.Name == "Charlie"
 })

 fmt.Printf("Item: %+v, Found: %v\n", item, found)

 fmt.Printf("FindDuplicatesBy: %v\n", lo.FindDuplicatesBy(p, func(p Person) int {
  return p.Age
 }))

 item, index, found := lo.FindIndexOf(p, func(p Person) bool {
  return p.Name == "Charlie"
 })

 fmt.Printf("Item: %+v, Index: %v, Found: %v\n", item, index, found)
}

Outputs:

Item: {Name:Charlie Age:35}, Found: true
FindDuplicatesBy: [{Alice 25}]
Item: {Name:Charlie Age:35}, Index: 2, Found: true

Find 方法同樣支持 map:

package main

import (
 "fmt"

 "github.com/samber/lo"
)

func main() {
 p := map[string]int{
  "Alice":   34,
  "Bob":     24,
  "Charlie": 34,
  "David":   29,
  "Eve":     34,
 }

 key, found := lo.FindKey(p, 34)
 fmt.Printf("Key: %v, Found: %v\n", key, found)
}

lo 的文檔地址 [4]

總結

這裏只展示了 github.com/samber/lo 庫約 10% 的方法。這個庫提供了許多簡化函數處理的實用工具。對於 Go 開發人員來說,這個庫是一個非常全面的工具包。

本文提供了一些在 Go 中處理集合時推薦的庫,希望對大家的開發工作有幫助。

參考資料

[1] slices: https://pkg.go.dev/slices?source=post_page-----8387cecdb8a4--------------------------------

[2] maps: https://pkg.go.dev/maps?source=post_page-----8387cecdb8a4--------------------------------

[3] pie: https://github.com/elliotchance/pie?source=post_page-----8387cecdb8a4--------------------------------

[4] lo: https://github.com/samber/lo?source=post_page-----8387cecdb8a4--------------------------------

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/iB_a4fsnZZSXILTqOEmQrQ