initial commit
This commit is contained in:
commit
19e0735f77
|
@ -0,0 +1,2 @@
|
|||
.idea/
|
||||
.DS_Store
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 prequist
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,25 @@
|
|||
package gomap
|
||||
|
||||
import (
|
||||
"github.com/prequist/ptypes"
|
||||
)
|
||||
|
||||
// NewBoxed creates a new boxed List.
|
||||
func NewBoxed(v ...interface{}) *List {
|
||||
// Return a list with the arguments handled.
|
||||
return New(handle(v...)...)
|
||||
}
|
||||
|
||||
// The handle function handles any variadic argument and
|
||||
// turns it into a boxed interface.
|
||||
func handle(v ...interface{}) []interface{} {
|
||||
// Create the new array.
|
||||
var arr []interface{}
|
||||
// Iterate.
|
||||
for _, value := range v {
|
||||
// Append the boxed type.
|
||||
arr = append(arr, ptypes.FromInterface(value))
|
||||
}
|
||||
// Return the new array.
|
||||
return arr
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
module github.com/prequist/gomap
|
||||
|
||||
go 1.16
|
||||
|
||||
require github.com/prequist/ptypes v0.0.0-20210818021037-1248b4a10d1e
|
|
@ -0,0 +1,2 @@
|
|||
github.com/prequist/ptypes v0.0.0-20210818021037-1248b4a10d1e h1:qz6N/gbrfIJ2XkVClea0S/KLQ+tfblXnyHRIBBJSIDI=
|
||||
github.com/prequist/ptypes v0.0.0-20210818021037-1248b4a10d1e/go.mod h1:RaioaJQo1X//0zLfYbKRlG3ltAGYKiRJ0PcOieuKRj0=
|
|
@ -0,0 +1,28 @@
|
|||
package gomap
|
||||
|
||||
// The Iterator is a small struct
|
||||
// used for handling iterator-like usage.
|
||||
// Go itself does not support the while loop,
|
||||
// however, we can achieve similar usage recursively.
|
||||
type Iterator struct {
|
||||
// The elements.
|
||||
elements []interface{}
|
||||
// The current index.
|
||||
current int
|
||||
// The current value.
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// Next gets the next value in the iterator
|
||||
func (it *Iterator) Next() (bool, interface{}) {
|
||||
// If there are no more values left, return false and nothing.
|
||||
if len(it.elements) <= it.current+1 {
|
||||
return false, nil
|
||||
}
|
||||
// Upgrade the current index.
|
||||
it.current = it.current + 1
|
||||
// Set the current value.
|
||||
it.Value = it.elements[it.current]
|
||||
// Return true, and the value.
|
||||
return true, it.Value
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package gomap
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIterator(t *testing.T) {
|
||||
v1 := "a"
|
||||
v2 := "b"
|
||||
v3 := "c"
|
||||
list := New(v1, v2, v3)
|
||||
if ok, value := list.Iterator().Next(); !ok && value != v1 {
|
||||
t.Error("the expected argument was not the given argument")
|
||||
}
|
||||
if ok, value := list.Iterator().Next(); !ok && value != v2 {
|
||||
t.Error("the expected argument was not the given argument")
|
||||
}
|
||||
if ok, value := list.Iterator().Next(); !ok && value != v3 {
|
||||
t.Error("the expected argument was not the given argument")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIteratorNext(b *testing.B) {
|
||||
v1 := "a"
|
||||
v2 := "b"
|
||||
v3 := "c"
|
||||
list := New(v1, v2, v3)
|
||||
for i := 0; i < b.N; i++ {
|
||||
list.Iterator().Next()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package gomap
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// New creates a new list with the given elements.
|
||||
func New(v ...interface{}) *List {
|
||||
// Create the list and return.
|
||||
return &List{elements: v}
|
||||
}
|
||||
|
||||
// The list primitive, private type alias.
|
||||
// listPrimitive is used as an alias for the
|
||||
// `interface{}` array, handling all elements.
|
||||
type listPrimitive []interface{}
|
||||
|
||||
// The List struct is the actual list.
|
||||
type List struct {
|
||||
// The concurrency lock, mu.
|
||||
mu sync.Mutex
|
||||
// The list elements.
|
||||
elements listPrimitive
|
||||
// The list's iterator.
|
||||
// The iterator should be accessed via the function
|
||||
// Iterator().
|
||||
iterator *Iterator
|
||||
}
|
||||
|
||||
// Items returns the items in the list.
|
||||
func (list *List) Items() []interface{} {
|
||||
return list.elements
|
||||
}
|
||||
|
||||
// Add an item to the list.
|
||||
func (list *List) Add(v interface{}) *List {
|
||||
// Lock the mutex.
|
||||
list.mu.Lock()
|
||||
// Append the elements to the list and the iterator.
|
||||
list.elements = append(list.elements, v)
|
||||
list.iterator.elements = append(list.Iterator().elements, v)
|
||||
// Unlock the mutex.
|
||||
list.mu.Unlock()
|
||||
return list
|
||||
}
|
||||
|
||||
// The Remove function removes the item at the requested index.
|
||||
func (list *List) Remove(index int) *List {
|
||||
// If the length of the elements is within the index.
|
||||
if len(list.elements) <= index && index > -1 {
|
||||
// Lock the mutex.
|
||||
list.mu.Lock()
|
||||
// Set the element at the index to nil.
|
||||
list.elements[index] = nil
|
||||
// Unlock the mutex.
|
||||
defer list.mu.Unlock()
|
||||
}
|
||||
// Return the list.
|
||||
return list
|
||||
}
|
||||
|
||||
// Iterator returns the list's iterator. If it does not exist,
|
||||
// it will create a new one.
|
||||
func (list *List) Iterator() *Iterator {
|
||||
// If the iterator does not exist,
|
||||
// we can create a new one.
|
||||
if list.iterator == nil {
|
||||
list.iterator = &Iterator{
|
||||
// Add the list's elements.
|
||||
elements: list.elements,
|
||||
// Set the iterator index.
|
||||
current: -1,
|
||||
// Set the current iteration value.
|
||||
Value: nil,
|
||||
}
|
||||
}
|
||||
// Return the list's iterator.
|
||||
return list.iterator
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package gomap
|
||||
|
||||
// The Transformer is a type alias for
|
||||
// a mapping function.
|
||||
type Transformer func(v interface{}) interface{}
|
||||
|
||||
// A MappableList is able to apply
|
||||
// a Transformer to it's elements.
|
||||
type MappableList struct {
|
||||
// The embedded list.
|
||||
*List
|
||||
}
|
||||
|
||||
// The Map function is called on a MappableList to apply
|
||||
// the requested Transformer.
|
||||
func (l *MappableList) Map(transformer Transformer) *List {
|
||||
// Convert into the parent object.
|
||||
return Map(l.List, transformer)
|
||||
}
|
||||
|
||||
// The Map function handles mapping for a raw List.
|
||||
func Map(list *List, transformer Transformer) *List {
|
||||
// Get the iterator.
|
||||
itr := list.Iterator()
|
||||
// Utilise the iterator recursively, to map
|
||||
// the entire list.
|
||||
if next, value := itr.Next(); next {
|
||||
// Transform the item.
|
||||
list.Items()[itr.current] = transformer(value)
|
||||
// Remap everything.
|
||||
return Map(list, transformer)
|
||||
}
|
||||
// Return the list.
|
||||
return list
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
package gomap
|
||||
|
||||
import (
|
||||
"github.com/prequist/ptypes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test a plain list.
|
||||
func TestPlain(t *testing.T) {
|
||||
// Values.
|
||||
v1 := 1
|
||||
v2 := 2
|
||||
v3 := 3
|
||||
values := []int{v1, v2, v3}
|
||||
arr := Plain(v1, v2, v3)
|
||||
// Indexing.
|
||||
for index, v := range arr {
|
||||
// Checking.
|
||||
if v != values[index]+1 {
|
||||
t.Errorf("received %d, expected %d", v, values[index]+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmark the plain list.
|
||||
func BenchmarkPlain(b *testing.B) {
|
||||
// Values.
|
||||
v1 := 1
|
||||
v2 := 2
|
||||
v3 := 3
|
||||
values := []int{v1, v2, v3}
|
||||
// Start benchmarking.
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Create an array.
|
||||
arr := Plain(v1, v2, v3)
|
||||
// Iterate through.
|
||||
for index, v := range arr {
|
||||
if v != values[index]+1 {
|
||||
b.Errorf("received %d, expected %d", v, values[index]+1)
|
||||
}
|
||||
}
|
||||
// Start again.
|
||||
}
|
||||
}
|
||||
|
||||
// Test the boxed list.
|
||||
func TestBox(t *testing.T) {
|
||||
// Values.
|
||||
v1 := 1
|
||||
v2 := 2
|
||||
v3 := 3
|
||||
values := []int{v1, v2, v3}
|
||||
arr := Boxed(v1, v2, v3)
|
||||
// Indexing.
|
||||
for index, v := range arr {
|
||||
// Checking.
|
||||
if v != values[index]+2 {
|
||||
t.Errorf("received %d, expected %d", v, values[index]+2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmark the boxed list.
|
||||
func BenchmarkBox(b *testing.B) {
|
||||
// Values.
|
||||
v1 := 1
|
||||
v2 := 2
|
||||
v3 := 3
|
||||
values := []int{v1, v2, v3}
|
||||
// Benchmarking.
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Create a new list.
|
||||
arr := Boxed(v1, v2, v3)
|
||||
// Indexing.
|
||||
for index, v := range arr {
|
||||
// Checking.
|
||||
if v != values[index]+2 {
|
||||
b.Errorf("received %d, expected %d", v, values[index]+2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test a string list.
|
||||
func BenchmarkString(b *testing.B) {
|
||||
// Values.
|
||||
v1 := "hello"
|
||||
v2 := "world"
|
||||
v3 := "test"
|
||||
values := []string{v1, v2, v3}
|
||||
// Benchmarking.
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Create a list.
|
||||
arr := String(v1, v2, v3)
|
||||
for index, v := range arr {
|
||||
// Checking.
|
||||
if len(v) != len(values[index])+3 {
|
||||
b.Errorf("received size %d expected %d", len(v), len(values[index])+2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmark a boxed string list.
|
||||
func BenchmarkBoxedString(b *testing.B) {
|
||||
// Values.
|
||||
v1 := "hello"
|
||||
v2 := "world"
|
||||
v3 := "test"
|
||||
values := []string{v1, v2, v3}
|
||||
// Benchmarking.
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Create an array.
|
||||
arr := BoxedString(v1, v2, v3)
|
||||
// Checking.
|
||||
for index, v := range arr {
|
||||
if len(v) != len(values[index])+3 {
|
||||
b.Errorf("received size %d expected %d", len(v), len(values[index])+2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmark the add function.
|
||||
func BenchmarkAdd(b *testing.B) {
|
||||
v1 := "hello"
|
||||
v2 := "world"
|
||||
v3 := "test"
|
||||
arr := NewBoxed(v1, v2, v3)
|
||||
for i := 0; i < b.N; i++ {
|
||||
arr.Add("hello")
|
||||
}
|
||||
}
|
||||
|
||||
// Create a plain int list.
|
||||
func Plain(v ...interface{}) []int {
|
||||
e := New(v...)
|
||||
mappable := MappableList{e}
|
||||
mappable.Map(func(v interface{}) interface{} {
|
||||
return v.(int) + 1
|
||||
})
|
||||
arr := make([]int, len(e.Items()))
|
||||
for index, vi := range e.Items() {
|
||||
arr[index] = vi.(int)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// Create a plain string list.
|
||||
func String(v ...interface{}) []string {
|
||||
e := New(v...)
|
||||
mappable := MappableList{e}
|
||||
mappable.Map(func(v interface{}) interface{} {
|
||||
return v.(string) + "---"
|
||||
})
|
||||
arr := make([]string, len(e.Items()))
|
||||
for index, vi := range e.Items() {
|
||||
arr[index] = vi.(string)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// Create a boxed int list.
|
||||
func Boxed(v ...interface{}) []int {
|
||||
e := NewBoxed(v...)
|
||||
mappable := MappableList{e}
|
||||
mappable.Map(func(v interface{}) interface{} {
|
||||
ib := v.(ptypes.Box).IntBox()
|
||||
return ptypes.FromInt(*ib.Int() + 2)
|
||||
})
|
||||
arr := make([]int, len(e.Items()))
|
||||
for index, vi := range e.Items() {
|
||||
arr[index] = *vi.(ptypes.Box).IntBox().Int()
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// Create a boxed string list.
|
||||
func BoxedString(v ...interface{}) []string {
|
||||
e := NewBoxed(v...)
|
||||
mappable := MappableList{e}
|
||||
mappable.Map(func(v interface{}) interface{} {
|
||||
box := v.(ptypes.Box)
|
||||
ib, _ := box.String()
|
||||
return ptypes.FromString(ib + "---")
|
||||
})
|
||||
arr := make([]string, len(e.Items()))
|
||||
for index, vi := range e.Items() {
|
||||
box := vi.(ptypes.Box)
|
||||
str, _ := box.String()
|
||||
arr[index] = str
|
||||
}
|
||||
return arr
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
# gomap
|
||||
|
||||
Map functions utilizing basic interfaces, assertions, and ptypes.
|
||||
|
||||
## Usage
|
||||
|
||||
<details closed>
|
||||
<summary>Basic Setup</summary>
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/prequist/gomap"
|
||||
)
|
||||
|
||||
func Showcase() {
|
||||
// gomap.Transformer
|
||||
transformer := func(i interface{}) interface{} {
|
||||
// type assert the interface and add 1.
|
||||
return i.(int) + 1
|
||||
}
|
||||
// Create a new list
|
||||
list := gomap.New(1, 2, 3, 4)
|
||||
mappable := gomap.MappableList{list}
|
||||
// The outcome.
|
||||
outcome := mappable.Map(transformer)
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details closed>
|
||||
<summary>Boxed</summary>
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/prequist/gomap"
|
||||
"github.com/prequist/ptypes"
|
||||
)
|
||||
|
||||
func MakeBoxedAndConvert() []int {
|
||||
e := gomap.NewBoxed(1, 2, 3, 4, 5)
|
||||
mappable := gomap.MappableList{e}
|
||||
mappable.Map(func(v interface{}) interface{} {
|
||||
ib := v.(ptypes.Box).IntBox()
|
||||
return ptypes.FromInt(*ib.Int() + 2)
|
||||
})
|
||||
arr := make([]int, len(e.Items()))
|
||||
for index, vi := range e.Items() {
|
||||
arr[index] = *vi.(ptypes.Box).IntBox().Int()
|
||||
}
|
||||
return arr
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details closed>
|
||||
<summary>Using <code>interface{}</code>s</summary>
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/prequist/gomap"
|
||||
"github.com/prequist/ptypes"
|
||||
)
|
||||
|
||||
func MakeAndConvert() []int {
|
||||
e := gomap.New(1, 2, 3, 4, 5)
|
||||
mappable := gomap.MappableList{e}
|
||||
mappable.Map(func(v interface{}) interface{} {
|
||||
return v.(int) + 1
|
||||
})
|
||||
arr := make([]int, len(e.Items()))
|
||||
for index, vi := range e.Items() {
|
||||
arr[index] = vi.(int)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Benchmarks
|
||||
|
||||
These are probably a bit bloat over time, however, they're not horrendous (compared to other)
|
||||
implementations.
|
||||
|
||||
On Mac OSX (Monterey, M1)
|
||||
|
||||
### map_test.go
|
||||
|
||||
```
|
||||
goos: darwin
|
||||
goarch: arm64
|
||||
pkg: github.com/prequist/gomap
|
||||
BenchmarkPlain-8 12474228 100.0 ns/op
|
||||
BenchmarkBox-8 759380 1523 ns/op
|
||||
BenchmarkString-8 4029303 277.8 ns/op
|
||||
BenchmarkBoxedString-8 782708 1501 ns/op
|
||||
BenchmarkAdd-8 17820822 94.33 ns/op
|
||||
|
||||
```
|
||||
|
||||
### iterator_test.go
|
||||
|
||||
```
|
||||
BenchmarkIteratorNext-8 1000000000 0.5661 ns/op
|
||||
```
|
Loading…
Reference in New Issue