Example
First, simulate a business scenario, including three structures: order, product, and custom order, and the order contains multiple products:
type Order struct { Id string Products []Product } type Product struct { Id string Price int } type CustomOrder struct { Id string }
Initialize the simulation data:
var orders = []Order{ { Id: "o1", Products: []Product{ { Id: "p1", Price: 1, }, { Id: "p2", Price: 2, }, }, }, { Id: "o2", Products: []Product{ { Id: "p3", Price: 3, }, { Id: "p4", Price: 4, }, }, }, }
Next, do various operations on the order list:
// Filter orders with ID o2func TestFilter(t *) { res := Lists[Order](orders).Filter(func(o any) bool { return o.(Order).Id == "o2" }).Collect() (res) // [{o2 [{p3 3} {p4 4}]}] } // Map the order list into a custom order listfunc TestMap(t *) { res := Lists[CustomOrder](orders).Map(func(o any) any { return CustomOrder{ Id: "custom-" + o.(Order).Id, } }).Collect() (res) // [{custom-o1} {custom-o2}] } // Expand the products in each order and map them to custom ordersfunc TestFlatAndMap(t *) { res := Lists[CustomOrder](orders). Flat(func(o any) []any { return Lists[any](o.(Order).Products).ToList() }). Map(func(p any) any { return CustomOrder{ Id: "ProductId-" + p.(Product).Id, } }).Collect() (res) // [{ProductId-p1} {ProductId-p2} {ProductId-p3} {ProductId-p4}] } // Find the most expensive product of all ordersfunc TestMax(t *) { res, found := Lists[Product](orders). Flat(func(o any) []any { return Lists[any](o.(Order).Products).ToList() }). Max(func(i, j any) bool { return i.(Product).Price > j.(Product).Price }) (found, res) // true {p4 4} }
principle
type List[T any] struct { list []any }
Wrap native slices in go intoList[T]
Structure, specifically specifying the generics in itT
It is the final result element type, not the type of the original incoming slice.
This design is because go can only specify generics when constructing structures, soList[T]
The generic specified as the final result element type, and can be called after the operation is completedCollect()
Method to get the finalT
Type slices are convenient for later business logic.
Because go does not support defining generics in the receiver function, the parameters and return value types of all operation functions can only be defined asany
, and then convert it into a business structure in the function body, for example, abovei.(Product).Price
。
After that, each operation, such as Filter, Map, Flat, etc., will be returnedList[T]
The structure can realize chain operation.
accomplish
type List[T any] struct { list []any } func Lists[T any](items any) *List[T] { rv := (items) if () != { panic(("not supported type: %v, please use slice instead", ())) } l := () s := make([]any, 0, l) for i := 0; i < l; i++ { s = append(s, (i).Interface()) } return &List[T]{ list: s, } } func (s *List[T]) Filter(fn func(any) bool) *List[T] { l := make([]any, 0) for _, e := range { if fn(e) { l = append(l, e) } } = l return s } func (s *List[T]) Map(fn func(any) any) *List[T] { l := make([]any, 0) for _, element := range { l = append(l, fn(element)) } return &List[T]{ list: l, } } func (s *List[T]) Flat(fn func(any) []any) *List[T] { l := make([]any, 0) for _, element := range { l = append(l, fn(element)...) } return &List[T]{ list: l, } } func (s *List[T]) Sort(fn func(i, j any) bool) *List[T] { if len() <= 0 { return s } (, func(i, j int) bool { return fn([i], [j]) }) return s } func (s *List[T]) Max(fn func(i, j any) bool) (T, bool) { return (fn).FindFirst() } func (s *List[T]) FindFirst() (T, bool) { if len() <= 0 { var nonsense T return nonsense, false } return [0].(T), true } func (s *List[T]) ToList() []any { return } func (s *List[T]) Collect() []T { t := make([]T, 0) for _, a := range { t = append(t, a.(T)) } return t }
This is the end of this article about detailed explanation of Slice chain operations in Go. For more related content on Go Slice chain operations, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!