(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>
|
||||||
|
|
||||||
|
<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>
|
<details closed>
|
||||||
<summary>Boxed</summary>
|
<summary>Boxed</summary>
|
||||||
|
|
||||||
@ -48,7 +74,7 @@ import (
|
|||||||
|
|
||||||
func MakeBoxedAndConvert() []int {
|
func MakeBoxedAndConvert() []int {
|
||||||
e := gomap.NewBoxed(1, 2, 3, 4, 5)
|
e := gomap.NewBoxed(1, 2, 3, 4, 5)
|
||||||
mappable := gomap.MappableList{e}
|
mappable := e.Mappable()
|
||||||
mappable.Map(func(v interface{}) interface{} {
|
mappable.Map(func(v interface{}) interface{} {
|
||||||
ib := v.(ptypes.Box).IntBox()
|
ib := v.(ptypes.Box).IntBox()
|
||||||
return ptypes.FromInt(*ib.Int() + 2)
|
return ptypes.FromInt(*ib.Int() + 2)
|
||||||
@ -74,7 +100,7 @@ import (
|
|||||||
|
|
||||||
func MakeAndConvert() []int {
|
func MakeAndConvert() []int {
|
||||||
e := gomap.New(1, 2, 3, 4, 5)
|
e := gomap.New(1, 2, 3, 4, 5)
|
||||||
mappable := gomap.MappableList{e}
|
mappable := e.Mappable()
|
||||||
mappable.Map(func(v interface{}) interface{} {
|
mappable.Map(func(v interface{}) interface{} {
|
||||||
return v.(int) + 1
|
return v.(int) + 1
|
||||||
})
|
})
|
||||||
@ -114,3 +140,12 @@ BenchmarkAdd-8 17820822 94.33 ns/op
|
|||||||
```
|
```
|
||||||
BenchmarkIteratorNext-8 1000000000 0.5661 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()
|
list.mu.Lock()
|
||||||
// Append the elements to the list and the iterator.
|
// Append the elements to the list and the iterator.
|
||||||
list.elements = append(list.elements, v)
|
list.elements = append(list.elements, v)
|
||||||
it := list.Iterator()
|
|
||||||
*it.elements = append(*it.elements, v)
|
|
||||||
// Unlock the mutex.
|
// Unlock the mutex.
|
||||||
list.mu.Unlock()
|
list.mu.Unlock()
|
||||||
return list
|
return list
|
||||||
|
14
map.go
14
map.go
@ -11,6 +11,11 @@ type MappableList struct {
|
|||||||
*List
|
*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 Map function is called on a MappableList to apply
|
||||||
// the requested Transformer.
|
// the requested Transformer.
|
||||||
func (l *MappableList) Map(transformer Transformer) *List {
|
func (l *MappableList) Map(transformer Transformer) *List {
|
||||||
@ -24,12 +29,15 @@ func Map(list *List, transformer Transformer) *List {
|
|||||||
itr := list.Iterator()
|
itr := list.Iterator()
|
||||||
// Utilise the iterator recursively, to map
|
// Utilise the iterator recursively, to map
|
||||||
// the entire list.
|
// the entire list.
|
||||||
|
var mapped List
|
||||||
|
sorter:
|
||||||
if next, value := itr.Next(); next {
|
if next, value := itr.Next(); next {
|
||||||
// Transform the item.
|
// Transform the item.
|
||||||
list.Items()[itr.current] = transformer(value)
|
transformed := transformer(value)
|
||||||
|
mapped.Add(transformed)
|
||||||
// Remap everything.
|
// Remap everything.
|
||||||
return Map(list, transformer)
|
goto sorter
|
||||||
}
|
}
|
||||||
// Return the list.
|
// 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 (
|
import (
|
||||||
|
"github.com/prequist/gomap"
|
||||||
"github.com/prequist/ptypes"
|
"github.com/prequist/ptypes"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -126,7 +127,7 @@ func BenchmarkAdd(b *testing.B) {
|
|||||||
v1 := "hello"
|
v1 := "hello"
|
||||||
v2 := "world"
|
v2 := "world"
|
||||||
v3 := "test"
|
v3 := "test"
|
||||||
arr := NewBoxed(v1, v2, v3)
|
arr := gomap.NewBoxed(v1, v2, v3)
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
arr.Add("hello")
|
arr.Add("hello")
|
||||||
}
|
}
|
||||||
@ -134,9 +135,9 @@ func BenchmarkAdd(b *testing.B) {
|
|||||||
|
|
||||||
// Create a plain int list.
|
// Create a plain int list.
|
||||||
func Plain(v ...interface{}) []int {
|
func Plain(v ...interface{}) []int {
|
||||||
e := New(v...)
|
e := gomap.New(v...)
|
||||||
mappable := MappableList{e}
|
mappable := e.Mappable()
|
||||||
mappable.Map(func(v interface{}) interface{} {
|
e = mappable.Map(func(v interface{}) interface{} {
|
||||||
return v.(int) + 1
|
return v.(int) + 1
|
||||||
})
|
})
|
||||||
arr := make([]int, len(e.Items()))
|
arr := make([]int, len(e.Items()))
|
||||||
@ -148,9 +149,9 @@ func Plain(v ...interface{}) []int {
|
|||||||
|
|
||||||
// Create a plain string list.
|
// Create a plain string list.
|
||||||
func String(v ...interface{}) []string {
|
func String(v ...interface{}) []string {
|
||||||
e := New(v...)
|
e := gomap.New(v...)
|
||||||
mappable := MappableList{e}
|
mappable := e.Mappable()
|
||||||
mappable.Map(func(v interface{}) interface{} {
|
e = mappable.Map(func(v interface{}) interface{} {
|
||||||
return v.(string) + "---"
|
return v.(string) + "---"
|
||||||
})
|
})
|
||||||
arr := make([]string, len(e.Items()))
|
arr := make([]string, len(e.Items()))
|
||||||
@ -162,9 +163,9 @@ func String(v ...interface{}) []string {
|
|||||||
|
|
||||||
// Create a boxed int list.
|
// Create a boxed int list.
|
||||||
func Boxed(v ...interface{}) []int {
|
func Boxed(v ...interface{}) []int {
|
||||||
e := NewBoxed(v...)
|
e := gomap.NewBoxed(v...)
|
||||||
mappable := MappableList{e}
|
mappable := e.Mappable()
|
||||||
mappable.Map(func(v interface{}) interface{} {
|
e = mappable.Map(func(v interface{}) interface{} {
|
||||||
ib := v.(ptypes.Box).IntBox()
|
ib := v.(ptypes.Box).IntBox()
|
||||||
return ptypes.FromInt(*ib.Int() + 2)
|
return ptypes.FromInt(*ib.Int() + 2)
|
||||||
})
|
})
|
||||||
@ -177,9 +178,9 @@ func Boxed(v ...interface{}) []int {
|
|||||||
|
|
||||||
// Create a boxed string list.
|
// Create a boxed string list.
|
||||||
func BoxedString(v ...interface{}) []string {
|
func BoxedString(v ...interface{}) []string {
|
||||||
e := NewBoxed(v...)
|
e := gomap.NewBoxed(v...)
|
||||||
mappable := MappableList{e}
|
mappable := e.Mappable()
|
||||||
mappable.Map(func(v interface{}) interface{} {
|
e = mappable.Map(func(v interface{}) interface{} {
|
||||||
box := v.(ptypes.Box)
|
box := v.(ptypes.Box)
|
||||||
ib, _ := box.String()
|
ib, _ := box.String()
|
||||||
return ptypes.FromString(ib + "---")
|
return ptypes.FromString(ib + "---")
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
package gomap
|
package gomap_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/prequist/gomap"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestStringHandle tests the slice string handle.
|
// TestStringHandle tests the slice string handle.
|
||||||
func TestStringHandle(t *testing.T) {
|
func TestStringHandle(t *testing.T) {
|
||||||
values := []string{"a", "b", "c"}
|
values := []string{"a", "b", "c"}
|
||||||
list := NewFromPrimitiveSlice(values, values[0])
|
list := gomap.NewFromPrimitiveSlice(values, values[0])
|
||||||
for index, v := range list.Items() {
|
for index, v := range list.Items() {
|
||||||
if values[index] != v.(string) {
|
if values[index] != v.(string) {
|
||||||
t.Errorf("got %s, expected %s", 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.
|
// TestIntHandle tests the int handle.
|
||||||
func TestIntHandle(t *testing.T) {
|
func TestIntHandle(t *testing.T) {
|
||||||
values := []int{1, 2, 3}
|
values := []int{1, 2, 3}
|
||||||
list := NewFromPrimitiveSlice(values, values[0])
|
list := gomap.NewFromPrimitiveSlice(values, values[0])
|
||||||
if list == nil {
|
if list == nil {
|
||||||
t.Error("list was nil")
|
t.Error("list was nil")
|
||||||
return
|
return
|
||||||
@ -33,7 +34,7 @@ func TestIntHandle(t *testing.T) {
|
|||||||
// TestInt32Handle tests the int32 handle.
|
// TestInt32Handle tests the int32 handle.
|
||||||
func TestInt32Handle(t *testing.T) {
|
func TestInt32Handle(t *testing.T) {
|
||||||
values := []int32{1, 2, 3}
|
values := []int32{1, 2, 3}
|
||||||
list := NewFromPrimitiveSlice(values, values[0])
|
list := gomap.NewFromPrimitiveSlice(values, values[0])
|
||||||
if list == nil {
|
if list == nil {
|
||||||
t.Error("list was nil")
|
t.Error("list was nil")
|
||||||
return
|
return
|
||||||
@ -50,7 +51,7 @@ func TestUintHandle(t *testing.T) {
|
|||||||
// Create a slice.
|
// Create a slice.
|
||||||
values := []uint{1, 2, 3}
|
values := []uint{1, 2, 3}
|
||||||
// Create a list.
|
// Create a list.
|
||||||
list := NewFromPrimitiveSlice(values, values[0])
|
list := gomap.NewFromPrimitiveSlice(values, values[0])
|
||||||
// If the list is nil, error.
|
// If the list is nil, error.
|
||||||
if list == nil {
|
if list == nil {
|
||||||
t.Error("list was nil")
|
t.Error("list was nil")
|
||||||
@ -69,14 +70,14 @@ func TestUintHandleMap(t *testing.T) {
|
|||||||
return i.(uint) * 2
|
return i.(uint) * 2
|
||||||
}
|
}
|
||||||
values := []uint{1, 3, 5}
|
values := []uint{1, 3, 5}
|
||||||
list := NewFromPrimitiveSlice(values, values[0])
|
list := gomap.NewFromPrimitiveSlice(values, values[0])
|
||||||
for index, v := range list.Items() {
|
for index, v := range list.Items() {
|
||||||
if v != values[index] {
|
if v != values[index] {
|
||||||
t.Errorf("got %d, expected %d", v, values[index])
|
t.Errorf("got %d, expected %d", v, values[index])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mappable := MappableList{list}
|
mappable := list.Mappable()
|
||||||
mappable.Map(transformer)
|
list = mappable.Map(transformer)
|
||||||
for index, v := range list.Items() {
|
for index, v := range list.Items() {
|
||||||
if v != values[index] * 2 {
|
if v != values[index] * 2 {
|
||||||
t.Errorf("got %d, expected %d", 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.
|
// TestFloatHandle tests the float handle.
|
||||||
func TestFloatHandle(t *testing.T) {
|
func TestFloatHandle(t *testing.T) {
|
||||||
values := []float32{1.2222, 2.3333, 3.4444}
|
values := []float32{1.2222, 2.3333, 3.4444}
|
||||||
list := NewFromPrimitiveSlice(values, values[0])
|
list := gomap.NewFromPrimitiveSlice(values, values[0])
|
||||||
if list == nil {
|
if list == nil {
|
||||||
t.Error("list was nil")
|
t.Error("list was nil")
|
||||||
return
|
return
|
||||||
@ -102,7 +103,7 @@ func TestFloatHandle(t *testing.T) {
|
|||||||
// TestComplexHandle tests the complex handle.
|
// TestComplexHandle tests the complex handle.
|
||||||
func TestComplexHandle(t *testing.T) {
|
func TestComplexHandle(t *testing.T) {
|
||||||
values := []complex64{1, 2, 3}
|
values := []complex64{1, 2, 3}
|
||||||
list := NewFromPrimitiveSlice(values, values[0])
|
list := gomap.NewFromPrimitiveSlice(values, values[0])
|
||||||
if list == nil {
|
if list == nil {
|
||||||
t.Error("list was nil")
|
t.Error("list was nil")
|
||||||
return
|
return
|
||||||
@ -117,7 +118,7 @@ func TestComplexHandle(t *testing.T) {
|
|||||||
// TestComplexHandle tests the complex handle.
|
// TestComplexHandle tests the complex handle.
|
||||||
func TestUintptrHandle(t *testing.T) {
|
func TestUintptrHandle(t *testing.T) {
|
||||||
values := []uintptr{1, 2, 3}
|
values := []uintptr{1, 2, 3}
|
||||||
list := NewFromPrimitiveSlice(values, values[0])
|
list := gomap.NewFromPrimitiveSlice(values, values[0])
|
||||||
if list == nil {
|
if list == nil {
|
||||||
t.Error("list was nil")
|
t.Error("list was nil")
|
||||||
return
|
return
|
||||||
|
Loading…
Reference in New Issue
Block a user