自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

十分鐘了解Golang集合類型數(shù)據(jù)操作

開發(fā) 前端
Go 生態(tài)系統(tǒng)擁有豐富的工具,可以簡化和擴(kuò)展數(shù)據(jù)操作的功能。本文介紹了不同的方法來對切片、映射和通道執(zhí)行不同的操作。庫的選擇主要取決于項(xiàng)目的具體要求和開發(fā)人員對某些編程風(fēng)格的偏好。

集合是構(gòu)建任何應(yīng)用程序的重要組成部分,對于集合來說,常見的有以下幾類操作:

  • 轉(zhuǎn)換(transform) - 對集合中的每個元素應(yīng)用某種函數(shù)以創(chuàng)建新類型集合的操作;
  • 過濾(filter) - 從集合中選擇滿足特定條件的元素的操作;
  • 聚合(aggregation) - 從集合中計(jì)算出單一結(jié)果的操作,通常用于匯總;
  • 分類(sorting)/排序(ordering) - 根據(jù)某些標(biāo)準(zhǔn)重新排列集合元素的操作;
  • 訪問(access) - 根據(jù)元素屬性或位置檢索元素的操作;
  • 通用(utility) - 與集合有關(guān)的通用操作,但不一定能夠完全歸入某個類別。

盡管 Go 有很多優(yōu)點(diǎn),但它對高級集合操作的內(nèi)置支持相對有限,因此在需要的時候有必要使用第三方軟件包。本文將探討幾種流行的 Go 庫的特性和功能,它們增強(qiáng)了 Go 高效處理集合的能力,從而幫助讀者選擇合適的工具,簡化 Go 項(xiàng)目中的數(shù)據(jù)處理任務(wù)。

導(dǎo)言

我們回顧一下上述每個集合操作的常用方法。

轉(zhuǎn)換(transform)

  • Map - 對集合中的每個元素應(yīng)用一個函數(shù),并返回結(jié)果集合;
  • FlatMap - 將每個元素處理為元素列表,然后將所有列表扁平化為單個列表。

過濾(filter)

  • Filter - 刪除與過濾函數(shù)不匹配的元素;
  • Distinct - 刪除集合中的重復(fù)元素;
  • TakeWhile - 從頭開始返回滿足給定條件的元素;
  • DropWhile - 從頭開始刪除符合給定條件的元素,返回剩余部分。

聚合(aggregation)

  • Reduce - 使用給定函數(shù)合并集合中的所有元素,并返回合并結(jié)果;
  • Count - 返回滿足特定條件的元素?cái)?shù)量;
  • Sum - 計(jì)算集合中每個元素的數(shù)值屬性總和;
  • Max/Min - 確定元素屬性的最大或最小值;
  • Average - 計(jì)算集合中元素?cái)?shù)值屬性的平均值。

分類(sorting)/排序(ordering)

  • Sort - 根據(jù)比較規(guī)則對集合中的元素進(jìn)行排序;
  • Reverse - 逆序排列集合中的元素。

訪問(access)

  • Find - 返回與查詢函數(shù)匹配的第一個元素;
  • AtIndex - 檢索特定索引位置的元素。

通用(utility)

  • GroupBy - 根據(jù)索引鍵生成器函數(shù)將元素分類;
  • Partition - 根據(jù)分類函數(shù)將一個集合分成兩個集合:一個是滿足分類函數(shù)的元素集合,另一個是不滿足分類函數(shù)的元素集合;
  • Slice Operations - 像切片或分塊這樣的操作,可以自定義查看或分割集合的方法。

Go 內(nèi)置功能

在 Go 中,有幾種類型可以處理數(shù)據(jù)集合:

  • Arrays(數(shù)組) - 固定大小的元素集合。數(shù)組大小在聲明時定義 var myArray [5]int;
  • Slices(切片) - 動態(tài)大小的元素集合。切片建立在數(shù)組之上,但與數(shù)組不同的是,它們可以增大或縮小。聲明:mySlice = []int{1, 2, 3};
  • Maps(映射) - 鍵值對的集合。map 可以動態(tài)增長,但不保證鍵的順序。myMap := map[string]int{"first":1, "second":2} 創(chuàng)建了一個鍵為字符串、值為整數(shù)的 map。
  • Channels(通道) - 強(qiáng)類型通信原語,允許在程序之間共享數(shù)據(jù)。myChan := make(chan int) 創(chuàng)建了一個用于傳輸整數(shù)的通道。

Go 標(biāo)準(zhǔn)庫提供了可充當(dāng)或增強(qiáng)集合的其他數(shù)據(jù)結(jié)構(gòu)及工具,例如:

  • Heap - container/heap包為任何sort.Interface接口提供堆操作。堆是一棵樹,其屬性是每個節(jié)點(diǎn)都是其子樹中的最小值節(jié)點(diǎn);
  • List — container/list包實(shí)現(xiàn)了雙向鏈表;
  • Ring — container/ring包實(shí)現(xiàn)了對循環(huán)列表的操作。

作為 Go 標(biāo)準(zhǔn)庫的一部分,還有用于處理切片和映射的軟件包:

  • slices - 該軟件包定義了對任何類型的切片都可用的各種函數(shù);
  • maps - 該軟件包定義了對任何類型的映射都有用的各種函數(shù)。

通過內(nèi)置功能,可以對集合進(jìn)行如下操作:

  • 獲取數(shù)組/切片/映射的長度;
  • 通過索引/鍵訪問元素,"切片"一個片段;
  • 遍歷元素。
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[:])
}

該代碼將會打印如下內(nèi)容:

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]

讓我們回顧一下內(nèi)置庫的功能!

切片

從 Go 1.21 開始,Go 標(biāo)準(zhǔn)庫中出現(xiàn)了slices包,這是 Go 語言向前邁出的重要一步,但我仍然更喜歡使用外部庫來處理集合(你很快就會明白為什么)。讓我們回顧一下該庫是如何支持所有集合操作類的。

支持的集合

切片

轉(zhuǎn)換

本軟件包不支持此類操作。

過濾

本軟件包不支持此類操作。

聚合

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
  }),
 )
}

上述代碼將打?。?/p>

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

不支持其他聚合。

分類/排序

slices可以使用比較函數(shù)對切片進(jìn)行排序:

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)
}

上述代碼將打?。?/p>

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}]

對我來說,最大的缺點(diǎn)是排序會修改原始切片。如果該方法能返回一個新的排序切片,從而保留原始數(shù)組,那就更好了。

訪問

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在已排序切片中搜索目標(biāo),并返回找到目標(biāo)的位置或目標(biāo)在排序順序中出現(xiàn)的位置。同時它還返回一個布爾值,表示是否在片段中找到目標(biāo)。切片必須按遞增順序排序。

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)
}

上述代碼將打?。?/p>

Position of 3: 2. Found: true
Position of 6: 5. Found: false

通用

slices提供了多種通用函數(shù):

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
 }))
}

上述代碼將打?。?/p>

Compare: 2
Contains: true
Delete: [{C 3} {A 1} {B 2}]
Equal: false
Is Sorted: false

文檔

https://pkg.go.dev/slices[2]

結(jié)論

在 Go 1.21 中引入的slices軟件包代表了在處理切片方面的重大改進(jìn)。然而,這個軟件包還缺乏很多重要功能,由于省略了一些更高級的集合操作,因此仍需要第三方庫來滿足更復(fù)雜的數(shù)據(jù)處理需求。

映射

與slices類似,maps從 Go 1.21 開始出現(xiàn)在 Go 標(biāo)準(zhǔn)庫中。正如所期望的那樣,它定義了各種操作映射的方法。

支持的集合

映射

轉(zhuǎn)換

本軟件包不支持此類操作。

過濾

本軟件包不支持此類操作。

匯聚

本軟件包不支持此類操作。

分類/排序

本軟件包不支持此類操作。

訪問

本軟件包不支持此類操作。

通用

本軟件包支持一組通用操作:

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

文檔

https://pkg.go.dev/maps[3]

結(jié)論

maps包的功能比slices包更加有限。因此,如果需要對映射進(jìn)行更復(fù)雜的操作,幾乎肯定需要依賴第三方庫。

github.com/elliotchance/pie

這是我個人最喜歡的處理切片和映射的軟件包。它提供了獨(dú)特的語法,能讓我們無縫進(jìn)行鏈?zhǔn)讲僮?,提高代碼的可讀性和效率。

有四種方式使用該庫:

  • 純調(diào)用 - 只需調(diào)用提供所需參數(shù)的庫方法;
  • pie.Of - 對任何元素類型進(jìn)行鏈?zhǔn)蕉嘀夭僮鳎?/li>
  • pie.OfOrdered - 對數(shù)字和字符串類型進(jìn)行鏈?zhǔn)蕉嘀夭僮鳎?/li>
  • pie.OfNumeric - 僅對數(shù)字進(jìn)行鏈?zhǔn)蕉嘀夭僮鳌?/li>
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(),
 )
}

上述代碼將打?。?/p>

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]}

該庫的鏈?zhǔn)讲僮鞣浅S邢蓿驗(yàn)镸ap等函數(shù)應(yīng)返回相同類型的集合,因此我認(rèn)為純方法調(diào)用是使用該庫的最佳方式。

支持的集合

切片、映射

轉(zhuǎn)換

庫中的Map方法允許將每個元素從一種類型轉(zhuǎn)換為另一種類型:

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
   },
  ),
 )
}

上述代碼將打?。?/p>

Map: [C A D B]

此外,還可以找到將二維切片轉(zhuǎn)換為單維的方法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
     },
    ),
   ),
  ),
 )
}

上述代碼將打?。?/p>

Unique Tags: [b c d e a]

使用Keys或Values方法可以只獲取映射的鍵或值:

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))
}

上述代碼將打印:

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

過濾

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

package main

import (
 "fmt"

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

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

 fmt.Printf("Bottom 3: %v\n", pie.Bottom(v, 3))
 fmt.Printf("Drop top 3: %v\n", pie.DropTop(v, 3))
 fmt.Printf("Drop while 3: %v\n", pie.DropWhile(v, func(value int) bool { return value < 3 }))
 fmt.Printf("Filter even: %v\n", pie.Filter(v, func(value int) bool { return value%2 == 0 }))
 fmt.Printf("Filter not even: %v\n", pie.FilterNot(v, func(value int) bool { return value%2 == 0 }))
 fmt.Printf("Unique values: %v\n", pie.Unique(v))
}

上述代碼將打印:

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]

聚合

有一種通用方法可以進(jìn)行任何類型的聚合Reduce。我們來計(jì)算標(biāo)準(zhǔn)差:

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)
}

上述代碼將打?。?/p>

Standard deviation: 1.555635

Reduce方法第一次調(diào)用reducer時,第一個切片元素是累積值,第二個元素是值參數(shù)。這就是公式如此奇怪的原因。

從下面的示例中,可以找到另一種內(nèi)置聚合方法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))
}

上述代碼將打?。?/p>

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

分類/排序

pie提供了三種不同的方法來對切片進(jìn)行分類:

  • Sort - 工作原理類似于sort.Slice,但與sort.Slice不同的是,返回的切片會重新分配內(nèi)存,以避免修改輸入切片;
  • SortStableUsing - 工作原理類似于sort.SliceStable。不過,與sort.SliceStable不同的是,返回的切片會重新分配內(nèi)存,以避免修改輸入切片。
  • SortUsing - 工作原理類似于sort.Slice。但是,與sort.Slice不同的是,返回的切片會重新分配內(nèi)存,以避免修改輸入切片。
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)
}

上述代碼將會打印:

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

訪問

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包含大量用于處理切片的實(shí)用方法。僅舉幾例:

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

上述代碼將打印:

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}]

文檔

https://github.com/elliotchance/pie[4]https://pkg.go.dev/github.com/elliotchance/pie/v2[5]

結(jié)論

elliotchance/pie/v2庫提供了一套令人印象深刻的功能,極大簡化了 Go 語言中的切片處理,其用于操作和查詢切片數(shù)據(jù)的強(qiáng)大方法為開發(fā)人員提供了強(qiáng)大工具,提高了代碼的可讀性和效率。強(qiáng)烈建議所有 Go 開發(fā)人員在下一個項(xiàng)目中嘗試使用該庫。

github.com/samber/lo

這是另一個在 Go 中處理集合的流行庫。它在某些方面很像流行的 JavaScript 庫Lodash[6]。該庫基于泛型實(shí)現(xiàn),而不是反射。

支持的集合

切片、映射、通道

轉(zhuǎn)換

該庫支持用于切片的Map和FlatMap默認(rèn)方法:

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
   },
  ),
 )
}

上述代碼將打?。?/p>

Map: [C A D B]

下一個示例展示了FlatMap的使用:

下一個示例展示了 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
    },
   ),
  ),
 )
}

上述代碼將打印:

Unique Tags: [a b c d e]

此外還可以獲取映射鍵、值或?qū)⒂成滢D(zhuǎn)換到切片等:

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) }))
}

上述代碼將打印:

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]

過濾

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 }))
}

上述代碼將打?。?/p>

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

此外,還可以通過匿名函數(shù)過濾切片和映射:

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 }))
}

上述代碼將打印:

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

聚合

lo將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)
}

上述代碼將打?。?/p>

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))
}

上述代碼將打印:

Standard deviation: 1.555635

有一些有用的方法可以使用通道: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)
 }
}

上述代碼將打?。?/p>

0
1
2
5
3
6
4
7
8
9

再舉一個例子:

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)
  }
 }
}

上述代碼將打?。?/p>

0
1
2
0
1
2
0
1
2

分類/排序

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))
}

上述代碼將打印:

Reverse: [5 4 3 2 1]

訪問

可以找到幾種在切片中查找元素的方法:

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)
}

上述代碼將打?。?/p>

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

此外,還可以找到支持映射的方法:

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)
}

由于映射是無序結(jié)構(gòu),因此結(jié)果無法預(yù)測,可能會看到如下輸出:

Key: Charlie, Found: true
Key: Alice, Found: true
Key: Eve, Found: true

通用

可以在lo中找到大量方法。舉幾個例子:

package main

import (
 "fmt"

 "github.com/samber/lo"
)

func main() {
 v1 := []int{1, 2, 3, 4, 5}
 v2 := []int{3, 4, 5, 6, 7}

 fmt.Printf("Chunk: %v\n", lo.Chunk(v1, 3))
 fmt.Printf("Intersect: %v\n", lo.Intersect(v1, v2))
 fmt.Printf("Union: %v\n", lo.Union(v1, v2))

 diff1, diff2 := lo.Difference(v1, v2)
 fmt.Printf("Difference: %v, %v\n", diff1, diff2)
}

上述代碼將打?。?/p>

Chunk: [[1 2 3] [4 5]]
Intersect: [3 4 5]
Union: [1 2 3 4 5 6 7]
Difference: [1 2], [6 7]

文檔

https://github.com/samber/lo[7]https://pkg.go.dev/github.com/samber/lo[8]

結(jié)論

上面只演示了github.com/samber/lo庫中約 10% 的方法,還有很多其他工具可以簡化函數(shù)的使用,該庫是 Go 開發(fā)人員的綜合工具包,是優(yōu)化 Go 開發(fā)工作流程的寶貴財(cái)富。

結(jié)論

Go 生態(tài)系統(tǒng)擁有豐富的工具,可以簡化和擴(kuò)展數(shù)據(jù)操作的功能。本文介紹了不同的方法來對切片、映射和通道執(zhí)行不同的操作。庫的選擇主要取決于項(xiàng)目的具體要求和開發(fā)人員對某些編程風(fēng)格的偏好。

責(zé)任編輯:武曉燕 來源: DeepNoMind
相關(guān)推薦

2024-06-19 09:58:29

2015-11-06 11:03:36

2024-05-13 09:28:43

Flink SQL大數(shù)據(jù)

2023-07-15 18:26:51

LinuxABI

2024-11-07 16:09:53

2021-07-29 08:57:23

ViteReact模塊

2024-01-29 00:20:00

GolangGo代碼

2020-12-17 06:48:21

SQLkafkaMySQL

2009-11-03 11:01:45

VB.NET遠(yuǎn)程事件

2025-03-18 12:20:00

編程

2024-12-13 15:29:57

SpringSpringBeanJava

2019-04-01 14:59:56

負(fù)載均衡服務(wù)器網(wǎng)絡(luò)

2020-12-09 16:41:22

LinuxIT開發(fā)

2024-10-06 12:50:25

2021-09-07 09:40:20

Spark大數(shù)據(jù)引擎

2022-06-16 07:31:41

Web組件封裝HTML 標(biāo)簽

2023-04-12 11:18:51

甘特圖前端

2012-07-10 01:22:32

PythonPython教程

2015-09-06 09:22:24

框架搭建快速高效app

2023-11-30 10:21:48

虛擬列表虛擬列表工具庫
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號