diff --git a/README.md b/README.md index ebde66c..bfce3e8 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,54 @@ func ExampleOf() { // 2 // 1,2,3,4, } + +func ExampleOfSlice() { + var intArr = []int{1, 2, 3, 4} + stream.OfSlice(intArr).ForEach(func(e types.T) { + fmt.Printf("%d,", e) + }) + var nilArr []int + stream.OfSlice(nilArr).ForEach(func(e types.T) { + fmt.Printf("should not print") + }) + var strArr = []string{"a", "b"} + stream.OfSlice(strArr). + Map(func(e types.T) types.R { + return fmt.Sprintf("<%s>", e) + }). + ForEach(func(e types.T) { + fmt.Printf("%s,", e) + }) + // Output: + // 1,2,3,4,,, +} + +func ExampleOfMap() { + var m1 = map[int]string{ + 3: "c", + 2: "b", + 1: "a", + } + s := stream.OfMap(m1). + Map(func(e types.T) types.R { + p := e.(types.Pair) + p.First, p.Second = p.Second, p.First + return p + }). + Sorted(func(left types.T, right types.T) int { + p1 := left.(types.Pair) + p2 := right.(types.Pair) + return p1.Second.(int) - p2.Second.(int) + }). + ToSlice() + fmt.Println(s) + stream.OfMap(nil).ForEach(func(e types.T) { + fmt.Println("not print") + }) + // Output: + // [{a 1} {b 2} {c 3}] +} + func ExampleStream_Filter() { stream.Of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9). Filter(func(e types.T) bool { diff --git a/example_test.go b/example_test.go index 5f4d848..8121691 100644 --- a/example_test.go +++ b/example_test.go @@ -3,6 +3,7 @@ package stream_test import ( "fmt" "reflect" + "sort" "testing" "github.com/youthlin/stream" @@ -18,6 +19,27 @@ func ExampleSlice() { // []types.T{1, 2, 3} // []types.T{"abc", "###"} } + +func ExampleEntries() { + var m1 = map[int]string{ + 1: "a", + 2: "b", + 3: "c", + } + entries := stream.Entries(m1) + sort.Slice(entries, func(i, j int) bool { + return entries[i].First.(int) < entries[j].First.(int) + }) + fmt.Printf("%v\n", entries) + stream.Of(stream.Slice(entries)...).ReduceWith(map[string]int{}, func(acc types.R, e types.T) types.R { + pair := e.(types.Pair) + (acc.(map[string]int))[pair.Second.(string)] = pair.First.(int) + return acc + }) + // Output: + // [{1 a} {2 b} {3 c}] +} + func ExampleOf() { fmt.Println(stream.Of().Count()) fmt.Println(stream.Of(1).Count()) @@ -32,6 +54,54 @@ func ExampleOf() { // 2 // 1,2,3,4, } + +func ExampleOfSlice() { + var intArr = []int{1, 2, 3, 4} + stream.OfSlice(intArr).ForEach(func(e types.T) { + fmt.Printf("%d,", e) + }) + var nilArr []int + stream.OfSlice(nilArr).ForEach(func(e types.T) { + fmt.Printf("should not print") + }) + var strArr = []string{"a", "b"} + stream.OfSlice(strArr). + Map(func(e types.T) types.R { + return fmt.Sprintf("<%s>", e) + }). + ForEach(func(e types.T) { + fmt.Printf("%s,", e) + }) + // Output: + // 1,2,3,4,,, +} + +func ExampleOfMap() { + var m1 = map[int]string{ + 3: "c", + 2: "b", + 1: "a", + } + s := stream.OfMap(m1). + Map(func(e types.T) types.R { + p := e.(types.Pair) + p.First, p.Second = p.Second, p.First + return p + }). + Sorted(func(left types.T, right types.T) int { + p1 := left.(types.Pair) + p2 := right.(types.Pair) + return p1.Second.(int) - p2.Second.(int) + }). + ToSlice() + fmt.Println(s) + stream.OfMap(nil).ForEach(func(e types.T) { + fmt.Println("not print") + }) + // Output: + // [{a 1} {b 2} {c 3}] +} + func ExampleIterate() { // 0 1 1 2 3 5 8 // | | next diff --git a/factory.go b/factory.go index a02f214..c3bbf71 100644 --- a/factory.go +++ b/factory.go @@ -4,12 +4,14 @@ import ( "errors" "reflect" + "github.com/youthlin/stream/optional" "github.com/youthlin/stream/types" ) var ( // ErrNotSlice a error to panic when call Slice but argument is not slice ErrNotSlice = errors.New("not slice") + ErrNotMap = errors.New("not map") ) // Slice 把任意的切片类型转为[]T类型. 可用作 Of() 入参. @@ -28,12 +30,71 @@ func Slice(slice types.T) []types.T { return result } +// Entries 把任意的 map 类型转为 []Pair +// Entries return entries of a map as []types.Pair which `First` field is key, `Second` field is value +func Entries(mapValue types.T) []types.Pair { + if reflect.TypeOf(mapValue).Kind() != reflect.Map { + panic(ErrNotMap) + } + value := reflect.ValueOf(mapValue) + var result []types.Pair + var it = value.MapRange() + for it.Next() { + result = append(result, types.Pair{ + First: it.Key().Interface(), + Second: it.Value().Interface(), + }) + } + return result +} + // Of create a Stream from some element // It's recommend to pass pointer type cause the element may be copy at each operate func Of(elements ...types.T) Stream { return newHead(it(elements...)) } +// OfSlice return a Stream. the input parameter `slice` must be a slice. +// if input is nil, return a empty Stream( same as Of() ) +func OfSlice(slice types.T) Stream { + if optional.IsNil(slice) { + return Of() + } + if reflect.TypeOf(slice).Kind() != reflect.Slice { + panic(ErrNotSlice) + } + value := reflect.ValueOf(slice) + it := &sliceIt{ + base: &base{ + current: 0, + size: value.Len(), + }, + sliceValue: value, + } + return newHead(it) +} + +// OfMap return a Stream which element type is types.Pair. +// the input parameter `mapValue` must be a map or it will panic +// if mapValue is nil, return a empty Stream ( same as Of() ) +func OfMap(mapValue types.T) Stream { + if optional.IsNil(mapValue) { + return Of() + } + if reflect.TypeOf(mapValue).Kind() != reflect.Map { + panic(ErrNotMap) + } + value := reflect.ValueOf(mapValue) + it := &mapIt{ + base: &base{ + current: 0, + size: value.Len(), + }, + mapValue: value.MapRange(), + } + return newHead(it) +} + // Iterate create a Stream by a seed and an UnaryOperator func Iterate(seed types.T, operator types.UnaryOperator) Stream { return newHead(withSeed(seed, operator)) diff --git a/iterator.go b/iterator.go index c8fe4d6..f1f2323 100644 --- a/iterator.go +++ b/iterator.go @@ -1,6 +1,10 @@ package stream -import "github.com/youthlin/stream/types" +import ( + "reflect" + + "github.com/youthlin/stream/types" +) const unknownSize = -1 @@ -12,8 +16,11 @@ type iterator interface { func it(elements ...types.T) iterator { return &sliceIterator{ + base: &base{ + current: 0, + size: len(elements), + }, elements: elements, - current: 0, } } func withSeed(seed types.T, f types.UnaryOperator) iterator { @@ -35,19 +42,24 @@ func withRange(fromInclude, toExclude endpoint, step int) iterator { } } -// region sliceIterator +type base struct { + current int + size int +} -type sliceIterator struct { - elements []types.T - current int +func (b *base) GetSizeIfKnown() int64 { + return int64(b.size) } -func (s *sliceIterator) GetSizeIfKnown() int64 { - return int64(len(s.elements)) +func (b *base) HasNext() bool { + return b.current < b.size } -func (s *sliceIterator) HasNext() bool { - return s.current < len(s.elements) +// region sliceIterator + +type sliceIterator struct { + *base + elements []types.T } func (s *sliceIterator) Next() types.T { @@ -58,6 +70,37 @@ func (s *sliceIterator) Next() types.T { // endregion sliceIterator +// region sliceIt + +// sliceIt 切片迭代器 反射实现 +// sliceIt a slice iterator implement with reflect.Value +type sliceIt struct { + *base + sliceValue reflect.Value +} + +func (s *sliceIt) Next() types.T { + e := s.sliceValue.Index(s.current).Interface() + s.current++ + return e +} + +// endregion sliceIt + +type mapIt struct { + *base + mapValue *reflect.MapIter +} + +func (m *mapIt) Next() types.T { + m.base.current++ + m.mapValue.Next() + return types.Pair{ + First: m.mapValue.Key().Interface(), + Second: m.mapValue.Value().Interface(), + } +} + // region seedIt type seedIt struct { diff --git a/types/type.go b/types/type.go index 56743ef..be9eaf4 100644 --- a/types/type.go +++ b/types/type.go @@ -29,6 +29,10 @@ type ( // if left is greater then right, it returns a positive number; // if left is less then right, it returns a negative number; if the two input are equal, it returns 0 Comparator func(left T, right T) int + Pair struct { + First T + Second R + } ) var (