initial commit

This commit is contained in:
bfu4 2021-08-18 04:03:33 -04:00
commit 19e0735f77
No known key found for this signature in database
GPG Key ID: FD1D952871D22043
11 changed files with 529 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.idea/
.DS_Store

21
LICENSE Normal file
View File

@ -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.

25
box_list.go Normal file
View File

@ -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
}

5
go.mod Normal file
View File

@ -0,0 +1,5 @@
module github.com/prequist/gomap
go 1.16
require github.com/prequist/ptypes v0.0.0-20210818021037-1248b4a10d1e

2
go.sum Normal file
View File

@ -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=

28
iterator.go Normal file
View File

@ -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
}

31
iterator_test.go Normal file
View File

@ -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()
}
}

79
list.go Normal file
View File

@ -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
}

35
map.go Normal file
View File

@ -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
}

194
map_test.go Normal file
View File

@ -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
}

107
readme.md Normal file
View File

@ -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
```