diff --git a/functional.go b/functional.go index c7f5caf..81dedb7 100644 --- a/functional.go +++ b/functional.go @@ -1,6 +1,8 @@ package FunctionalLib -import Seq "github.com/geniussportsgroup/Slist" +import ( + Seq "github.com/geniussportsgroup/Slist" +) type SequentialIterator interface { ResetFirst() interface{} @@ -22,11 +24,103 @@ type Pair struct { Item1, Item2 interface{} } +type Tuple struct { + l *[]interface{} +} + +func NewTuple(items ...interface{}) *Tuple { + + s := make([]interface{}, 0, len(items)) + tuple := Tuple{l: &s} + for _, i := range items { + *tuple.l = append(*tuple.l, i) + } + return &tuple +} + +func BuildTuple(n int) *Tuple { + s := make([]interface{}, n, n) + return &Tuple{l: &s} +} + +func (tuple *Tuple) Set(i int, item interface{}) { + (*tuple.l)[i] = item +} + +func (tuple *Tuple) Traverse(f func(interface{}) bool) bool { + ptr := tuple.l + for i := 0; i < len(*tuple.l); i++ { + if !f((*ptr)[i]) { + return false + } + } + return true +} + +func (tuple *Tuple) Append(item interface{}, items ...interface{}) interface{} { + *tuple.l = append(*tuple.l, item) + for _, i := range items { + *tuple.l = append(*tuple.l, i) + } + return tuple +} + +func (tuple *Tuple) Size() int { + return len(*tuple.l) +} + +func (tuple *Tuple) Swap(other interface{}) interface{} { + otherTuple := other.(*Tuple) + tuple.l, otherTuple.l = otherTuple.l, tuple.l + return tuple +} + +func (tuple *Tuple) IsEmpty() bool { + return tuple.Size() == 0 +} + +type TupleIterator struct { + tuple *Tuple + pos int +} + +func (tuple *Tuple) CreateIterator() interface{} { + return &TupleIterator{ + tuple: tuple, + pos: 0, + } +} + +func NewTupleIterator(tuple Tuple) *TupleIterator { + return tuple.CreateIterator().(*TupleIterator) +} + +func (it *TupleIterator) HasCurr() bool { + return it.pos < len(*it.tuple.l) +} + +func (it *TupleIterator) GetCurr() interface{} { + return (*it.tuple.l)[it.pos] +} + +func (tuple *Tuple) Nth(i int) interface{} { + return (*tuple.l)[i] +} + +func (it *TupleIterator) Next() interface{} { + it.pos++ + return it +} + +func (it *TupleIterator) ResetFirst() interface{} { + it.pos = 0 + return it +} + // Execute operation receiving every item of the sequence. Return seq func ForEach(seq Sequence, operation func(interface{})) interface{} { seq.Traverse(func(i interface{}) bool { - operation(i) return true }) @@ -217,3 +311,48 @@ func Position(seq Sequence, predicate func(item interface{}) bool) int { return -1 } + +// Zip all the lists into a list of tuples +func TZip(list *Seq.Slist, lists ...*Seq.Slist) *Seq.Slist { + + sz := len(lists) + 1 + ret := Seq.New() + + for it := Seq.NewIterator(list); it.HasCurr(); it.Next() { + tuple := BuildTuple(sz) + tuple.Set(0, it.GetCurr()) + ret.Append(tuple) + } + + for i, l := range lists { + + zipL := Zip(ret, l) + for it := Seq.NewIterator(zipL); it.HasCurr(); it.Next() { // traverse i-th list + pair := it.GetCurr().(Pair) + tuple := pair.Item1.(*Tuple) + item := pair.Item2 + tuple.Set(i+1, item) + } + } + + return ret +} + +// Unzip a list of tuples into a tuple of lists +func TUnzip(tupleList *Seq.Slist) *Tuple { + + tupleSize := tupleList.First().(*Tuple).Size() + result := BuildTuple(tupleSize) + for i := 0; i < tupleSize; i++ { + result.Set(i, Seq.New()) + } + + for it := Seq.NewIterator(tupleList); it.HasCurr(); it.Next() { + tuple := it.GetCurr().(*Tuple) + for i := 0; i < tuple.Size(); i++ { + result.Nth(i).(*Seq.Slist).Append(tuple.Nth(i)) + } + } + + return result +} diff --git a/functional_test.go b/functional_test.go index f18e57b..9e862af 100644 --- a/functional_test.go +++ b/functional_test.go @@ -229,3 +229,74 @@ func TestPosition(t *testing.T) { }), i) } } + +func TestNewTuple(t *testing.T) { + + tuple4 := NewTuple(1, 2, 3, 4) + assert.Equal(t, tuple4.Size(), 4) + assert.Equal(t, tuple4.Nth(0).(int), 1) +} + +func TestTZip(t *testing.T) { + + l1 := Seq.New(1, 2, 3, 4, 5) + l2 := Seq.New("A", "B", "C", "D", "E") + l3 := Seq.New(-5, -4, -3, -2, -1) + + zl := TZip(l1, l2, l3) + + assert.Equal(t, zl.Size(), l1.Size()) + + assert.True(t, All(Zip(zl, l1), func(item interface{}) bool { + pair := item.(Pair) + tuple := pair.Item1.(*Tuple) + i := pair.Item2.(int) + return tuple.Nth(0) == i + })) + + assert.True(t, All(Zip(zl, l2), func(item interface{}) bool { + pair := item.(Pair) + tuple := pair.Item1.(*Tuple) + str := pair.Item2.(string) + return tuple.Nth(1) == str + })) + + assert.True(t, All(Zip(zl, l3), func(item interface{}) bool { + pair := item.(Pair) + tuple := pair.Item1.(*Tuple) + i := pair.Item2.(int) + return tuple.Nth(2) == i + })) +} + +func TestTUnzip(t *testing.T) { + + l1 := Seq.New(1, 2, 3, 4, 5) + l2 := Seq.New("A", "B", "C", "D", "E") + l3 := Seq.New(-5, -4, -3, -2, -1) + + zl := TZip(l1, l2, l3) + + tuple := TUnzip(zl) + + assert.True(t, All(Zip(tuple.Nth(0).(*Seq.Slist), l1), func(p interface{}) bool { + pair := p.(Pair) + i1 := pair.Item1.(int) + i2 := pair.Item2.(int) + return i1 == i2 + })) + + assert.True(t, All(Zip(tuple.Nth(1).(*Seq.Slist), l2), func(p interface{}) bool { + pair := p.(Pair) + i1 := pair.Item1.(string) + i2 := pair.Item2.(string) + return i1 == i2 + })) + + assert.True(t, All(Zip(tuple.Nth(2).(*Seq.Slist), l3), func(p interface{}) bool { + pair := p.(Pair) + i1 := pair.Item1.(int) + i2 := pair.Item2.(int) + return i1 == i2 + })) +}