(feat): filter function, list#Mappable() (#1)
This commit is contained in:
commit
93af011977
39
README.md
39
README.md
@ -37,6 +37,32 @@ func Showcase() {
|
||||
|
||||
</details>
|
||||
|
||||
<details close>
|
||||
<summary>Filter</summary>
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/prequist/gomap"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Showcase() {
|
||||
e := gomap.New("a", "b", "c", "ab", "d")
|
||||
predicate := func(i interface{}) bool {
|
||||
if str, ok := i.(string) {
|
||||
return strings.Contains(str, "a")
|
||||
}
|
||||
return false
|
||||
}
|
||||
// Get the mappable list
|
||||
mappable := e.Mappable()
|
||||
// Apply the predicate, return the filtered list as a variable
|
||||
mappable = e.Filter(predicate) // "a", "ab"
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details closed>
|
||||
<summary>Boxed</summary>
|
||||
|
||||
@ -48,7 +74,7 @@ import (
|
||||
|
||||
func MakeBoxedAndConvert() []int {
|
||||
e := gomap.NewBoxed(1, 2, 3, 4, 5)
|
||||
mappable := gomap.MappableList{e}
|
||||
mappable := e.Mappable()
|
||||
mappable.Map(func(v interface{}) interface{} {
|
||||
ib := v.(ptypes.Box).IntBox()
|
||||
return ptypes.FromInt(*ib.Int() + 2)
|
||||
@ -74,7 +100,7 @@ import (
|
||||
|
||||
func MakeAndConvert() []int {
|
||||
e := gomap.New(1, 2, 3, 4, 5)
|
||||
mappable := gomap.MappableList{e}
|
||||
mappable := e.Mappable()
|
||||
mappable.Map(func(v interface{}) interface{} {
|
||||
return v.(int) + 1
|
||||
})
|
||||
@ -113,4 +139,13 @@ BenchmarkAdd-8 17820822 94.33 ns/op
|
||||
|
||||
```
|
||||
BenchmarkIteratorNext-8 1000000000 0.5661 ns/op
|
||||
```
|
||||
|
||||
|
||||
### filter_test.go
|
||||
|
||||
```
|
||||
BenchmarkFilter
|
||||
BenchmarkFilter/Filter
|
||||
BenchmarkFilter/Filter-8 1000000000 0.0000009 ns/op
|
||||
```
|
29
filter.go
Normal file
29
filter.go
Normal file
@ -0,0 +1,29 @@
|
||||
package gomap
|
||||
|
||||
// The Predicate is a type alias for a function that
|
||||
// takes in a parameter and returns a boolean.
|
||||
// The predicate is similar to Java's `Predicate<T>`,
|
||||
// without the generic usage. This makes the usage of a predicate not
|
||||
// necessarily type safe. This also means that the writer of the predicate
|
||||
// has to write code that they trust when passing the predicate into the filter.
|
||||
type Predicate func(interface{}) bool
|
||||
|
||||
func (l *MappableList) Filter(predicate Predicate) MappableList {
|
||||
// Get the iterator.
|
||||
itr := l.Iterator()
|
||||
// Utilise the iterator recursively, to map
|
||||
// the entire list.
|
||||
var newArray List
|
||||
// We utilize the goto call to avoid recursively calling the filter function.
|
||||
sorter:
|
||||
if next, value := itr.Next(); next {
|
||||
// Filter by the predicate.
|
||||
if predicate(value) {
|
||||
newArray.Add(value)
|
||||
}
|
||||
// Jump back to the sorter label.
|
||||
goto sorter
|
||||
}
|
||||
// Return the list.
|
||||
return MappableList{&newArray}
|
||||
}
|
54
filter_test.go
Normal file
54
filter_test.go
Normal file
@ -0,0 +1,54 @@
|
||||
package gomap_test
|
||||
|
||||
import (
|
||||
"github.com/prequist/gomap"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test the filtering function MappableList.Filter(Predicate).
|
||||
func TestFilter(t *testing.T) {
|
||||
stringMap := gomap.New("test", "test1", "test2", "not_applicable", "test3")
|
||||
mappable := stringMap.Mappable()
|
||||
filtered := mappable.Filter(func(i interface{}) bool {
|
||||
if str, ok := i.(string); ok {
|
||||
return strings.Contains(str, "test")
|
||||
}
|
||||
return false
|
||||
})
|
||||
filteredItems := mapToString(filtered.Items())
|
||||
knownKeys := []string{"test", "test1", "test2", "test3"}
|
||||
if !reflect.DeepEqual(filteredItems, knownKeys) {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmark the filtering function.
|
||||
func BenchmarkFilter(b *testing.B) {
|
||||
stringMap := gomap.New("test", "test1", "test2", "not_applicable", "test3")
|
||||
mappable := stringMap.Mappable()
|
||||
// Benchmark the actual filtering.
|
||||
b.Run("Filter", func(b *testing.B) {
|
||||
mappable = mappable.Filter(func(i interface{}) bool {
|
||||
if str, ok := i.(string); ok {
|
||||
return strings.Contains(str, "test")
|
||||
}
|
||||
return false
|
||||
})
|
||||
})
|
||||
filteredItems := mapToString(mappable.Items())
|
||||
knownKeys := []string{"test", "test1", "test2", "test3"}
|
||||
if !reflect.DeepEqual(filteredItems, knownKeys) {
|
||||
b.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
// Map an interface array to a string.
|
||||
func mapToString(i []interface{}) []string {
|
||||
var strArr []string
|
||||
for _, v := range i {
|
||||
strArr = append(strArr, v.(string))
|
||||
}
|
||||
return strArr
|
||||
}
|
2
list.go
2
list.go
@ -38,8 +38,6 @@ func (list *List) Add(v interface{}) *List {
|
||||
list.mu.Lock()
|
||||
// Append the elements to the list and the iterator.
|
||||
list.elements = append(list.elements, v)
|
||||
it := list.Iterator()
|
||||
*it.elements = append(*it.elements, v)
|
||||
// Unlock the mutex.
|
||||
list.mu.Unlock()
|
||||
return list
|
||||
|
14
map.go
14
map.go
@ -11,6 +11,11 @@ type MappableList struct {
|
||||
*List
|
||||
}
|
||||
|
||||
// Mappable returns a mappable list from an existing List.
|
||||
func (list *List) Mappable() MappableList {
|
||||
return MappableList{list}
|
||||
}
|
||||
|
||||
// The Map function is called on a MappableList to apply
|
||||
// the requested Transformer.
|
||||
func (l *MappableList) Map(transformer Transformer) *List {
|
||||
@ -24,12 +29,15 @@ func Map(list *List, transformer Transformer) *List {
|
||||
itr := list.Iterator()
|
||||
// Utilise the iterator recursively, to map
|
||||
// the entire list.
|
||||
var mapped List
|
||||
sorter:
|
||||
if next, value := itr.Next(); next {
|
||||
// Transform the item.
|
||||
list.Items()[itr.current] = transformer(value)
|
||||
transformed := transformer(value)
|
||||
mapped.Add(transformed)
|
||||
// Remap everything.
|
||||
return Map(list, transformer)
|
||||
goto sorter
|
||||
}
|
||||
// Return the list.
|
||||
return list
|
||||
return &mapped
|
||||
}
|
||||
|
29
map_test.go
29
map_test.go
@ -1,6 +1,7 @@
|
||||
package gomap
|
||||
package gomap_test
|
||||
|
||||
import (
|
||||
"github.com/prequist/gomap"
|
||||
"github.com/prequist/ptypes"
|
||||
"testing"
|
||||
)
|
||||
@ -126,7 +127,7 @@ func BenchmarkAdd(b *testing.B) {
|
||||
v1 := "hello"
|
||||
v2 := "world"
|
||||
v3 := "test"
|
||||
arr := NewBoxed(v1, v2, v3)
|
||||
arr := gomap.NewBoxed(v1, v2, v3)
|
||||
for i := 0; i < b.N; i++ {
|
||||
arr.Add("hello")
|
||||
}
|
||||
@ -134,9 +135,9 @@ func BenchmarkAdd(b *testing.B) {
|
||||
|
||||
// Create a plain int list.
|
||||
func Plain(v ...interface{}) []int {
|
||||
e := New(v...)
|
||||
mappable := MappableList{e}
|
||||
mappable.Map(func(v interface{}) interface{} {
|
||||
e := gomap.New(v...)
|
||||
mappable := e.Mappable()
|
||||
e = mappable.Map(func(v interface{}) interface{} {
|
||||
return v.(int) + 1
|
||||
})
|
||||
arr := make([]int, len(e.Items()))
|
||||
@ -148,9 +149,9 @@ func Plain(v ...interface{}) []int {
|
||||
|
||||
// Create a plain string list.
|
||||
func String(v ...interface{}) []string {
|
||||
e := New(v...)
|
||||
mappable := MappableList{e}
|
||||
mappable.Map(func(v interface{}) interface{} {
|
||||
e := gomap.New(v...)
|
||||
mappable := e.Mappable()
|
||||
e = mappable.Map(func(v interface{}) interface{} {
|
||||
return v.(string) + "---"
|
||||
})
|
||||
arr := make([]string, len(e.Items()))
|
||||
@ -162,9 +163,9 @@ func String(v ...interface{}) []string {
|
||||
|
||||
// Create a boxed int list.
|
||||
func Boxed(v ...interface{}) []int {
|
||||
e := NewBoxed(v...)
|
||||
mappable := MappableList{e}
|
||||
mappable.Map(func(v interface{}) interface{} {
|
||||
e := gomap.NewBoxed(v...)
|
||||
mappable := e.Mappable()
|
||||
e = mappable.Map(func(v interface{}) interface{} {
|
||||
ib := v.(ptypes.Box).IntBox()
|
||||
return ptypes.FromInt(*ib.Int() + 2)
|
||||
})
|
||||
@ -177,9 +178,9 @@ func Boxed(v ...interface{}) []int {
|
||||
|
||||
// Create a boxed string list.
|
||||
func BoxedString(v ...interface{}) []string {
|
||||
e := NewBoxed(v...)
|
||||
mappable := MappableList{e}
|
||||
mappable.Map(func(v interface{}) interface{} {
|
||||
e := gomap.NewBoxed(v...)
|
||||
mappable := e.Mappable()
|
||||
e = mappable.Map(func(v interface{}) interface{} {
|
||||
box := v.(ptypes.Box)
|
||||
ib, _ := box.String()
|
||||
return ptypes.FromString(ib + "---")
|
||||
|
@ -1,13 +1,14 @@
|
||||
package gomap
|
||||
package gomap_test
|
||||
|
||||
import (
|
||||
"github.com/prequist/gomap"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestStringHandle tests the slice string handle.
|
||||
func TestStringHandle(t *testing.T) {
|
||||
values := []string{"a", "b", "c"}
|
||||
list := NewFromPrimitiveSlice(values, values[0])
|
||||
list := gomap.NewFromPrimitiveSlice(values, values[0])
|
||||
for index, v := range list.Items() {
|
||||
if values[index] != v.(string) {
|
||||
t.Errorf("got %s, expected %s", values[index], v.(string))
|
||||
@ -18,7 +19,7 @@ func TestStringHandle(t *testing.T) {
|
||||
// TestIntHandle tests the int handle.
|
||||
func TestIntHandle(t *testing.T) {
|
||||
values := []int{1, 2, 3}
|
||||
list := NewFromPrimitiveSlice(values, values[0])
|
||||
list := gomap.NewFromPrimitiveSlice(values, values[0])
|
||||
if list == nil {
|
||||
t.Error("list was nil")
|
||||
return
|
||||
@ -33,7 +34,7 @@ func TestIntHandle(t *testing.T) {
|
||||
// TestInt32Handle tests the int32 handle.
|
||||
func TestInt32Handle(t *testing.T) {
|
||||
values := []int32{1, 2, 3}
|
||||
list := NewFromPrimitiveSlice(values, values[0])
|
||||
list := gomap.NewFromPrimitiveSlice(values, values[0])
|
||||
if list == nil {
|
||||
t.Error("list was nil")
|
||||
return
|
||||
@ -50,7 +51,7 @@ func TestUintHandle(t *testing.T) {
|
||||
// Create a slice.
|
||||
values := []uint{1, 2, 3}
|
||||
// Create a list.
|
||||
list := NewFromPrimitiveSlice(values, values[0])
|
||||
list := gomap.NewFromPrimitiveSlice(values, values[0])
|
||||
// If the list is nil, error.
|
||||
if list == nil {
|
||||
t.Error("list was nil")
|
||||
@ -69,14 +70,14 @@ func TestUintHandleMap(t *testing.T) {
|
||||
return i.(uint) * 2
|
||||
}
|
||||
values := []uint{1, 3, 5}
|
||||
list := NewFromPrimitiveSlice(values, values[0])
|
||||
list := gomap.NewFromPrimitiveSlice(values, values[0])
|
||||
for index, v := range list.Items() {
|
||||
if v != values[index] {
|
||||
t.Errorf("got %d, expected %d", v, values[index])
|
||||
}
|
||||
}
|
||||
mappable := MappableList{list}
|
||||
mappable.Map(transformer)
|
||||
mappable := list.Mappable()
|
||||
list = mappable.Map(transformer)
|
||||
for index, v := range list.Items() {
|
||||
if v != values[index] * 2 {
|
||||
t.Errorf("got %d, expected %d", v, values[index] * 2)
|
||||
@ -87,7 +88,7 @@ func TestUintHandleMap(t *testing.T) {
|
||||
// TestFloatHandle tests the float handle.
|
||||
func TestFloatHandle(t *testing.T) {
|
||||
values := []float32{1.2222, 2.3333, 3.4444}
|
||||
list := NewFromPrimitiveSlice(values, values[0])
|
||||
list := gomap.NewFromPrimitiveSlice(values, values[0])
|
||||
if list == nil {
|
||||
t.Error("list was nil")
|
||||
return
|
||||
@ -102,7 +103,7 @@ func TestFloatHandle(t *testing.T) {
|
||||
// TestComplexHandle tests the complex handle.
|
||||
func TestComplexHandle(t *testing.T) {
|
||||
values := []complex64{1, 2, 3}
|
||||
list := NewFromPrimitiveSlice(values, values[0])
|
||||
list := gomap.NewFromPrimitiveSlice(values, values[0])
|
||||
if list == nil {
|
||||
t.Error("list was nil")
|
||||
return
|
||||
@ -117,7 +118,7 @@ func TestComplexHandle(t *testing.T) {
|
||||
// TestComplexHandle tests the complex handle.
|
||||
func TestUintptrHandle(t *testing.T) {
|
||||
values := []uintptr{1, 2, 3}
|
||||
list := NewFromPrimitiveSlice(values, values[0])
|
||||
list := gomap.NewFromPrimitiveSlice(values, values[0])
|
||||
if list == nil {
|
||||
t.Error("list was nil")
|
||||
return
|
||||
|
Loading…
Reference in New Issue
Block a user