Skip to content

Commit

Permalink
stream add methods:
Browse files Browse the repository at this point in the history
OfSlice, OfMap
  • Loading branch information
youthlin authored and youthlin committed Dec 7, 2020
1 parent a227492 commit 1751187
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 10 deletions.
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,<a>,<b>,
}

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 {
Expand Down
70 changes: 70 additions & 0 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package stream_test
import (
"fmt"
"reflect"
"sort"
"testing"

"github.com/youthlin/stream"
Expand All @@ -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())
Expand All @@ -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,<a>,<b>,
}

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
Expand Down
61 changes: 61 additions & 0 deletions factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() 入参.
Expand All @@ -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))
Expand Down
63 changes: 53 additions & 10 deletions iterator.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package stream

import "github.com/youthlin/stream/types"
import (
"reflect"

"github.com/youthlin/stream/types"
)

const unknownSize = -1

Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions types/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down

0 comments on commit 1751187

Please sign in to comment.