Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/slice abstractions #27

Draft
wants to merge 5 commits into
base: feature/generics
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions lib/slice.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package lib

import "fmt"

type Slice[T any] []T
type Predicate[T any] func (iteratee T) bool

// Filter returns slice items matching specified predicate
func (slice Slice[T]) Filter(predicate Predicate[T]) Slice[T] {
filtered := Slice[T]{}
for _, v := range slice {
if predicate == nil || predicate(v) {
filtered = append(filtered, v)
}
}
return filtered
}
Comment on lines +7 to +15
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
func (slice Slice[T]) Filter(predicate Predicate[T]) Slice[T] {
filtered := Slice[T]{}
for _, v := range slice {
if predicate == nil || predicate(v) {
filtered = append(filtered, v)
}
}
return filtered
}
func Filter(slice Slice[T], predicate Predicate[T]) Slice[T] {
filtered := Slice[T]{}
for _, v := range slice {
if predicate == nil || predicate(v) {
filtered = append(filtered, v)
}
}
return filtered
}

i think this type of params maybe better for utilization in code

Copy link
Author

@schonmann schonmann Feb 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you be more specific on the reasons why? I aggree that this could have less friction with legacy code, but at this point we should question what is indeed idiomatic in Golang. It seems quite reasonable that generic type abstractions like Slice[T] or Map[TK,TV] would be a better choice for 1.18+ Golang code, as custom, reusable behaviour can be easily added to satistfy application needs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think is better have less friction with legacy code, cuz the community probably will make something similar to this PR in the std lib, so we can make an agnostic function that we recive any slice is better for now


// First returns the first item matching specified predicate
func (slice Slice[T]) First(predicate Predicate[T]) *T {
for i, v := range slice {
if predicate == nil || predicate(v) {
return &slice[i]
}
}
return nil
}

// Last returns the last item matching specified predicate
func (slice Slice[T]) Last(predicate Predicate[T]) *T {
for i := len(slice)-1; i >= 0; i-- {
if predicate == nil || predicate(slice[i]) {
return &slice[i]
}
}
return nil
}

// Any returns if any item matches the specified predicate
func (slice Slice[T]) Any(predicate Predicate[T]) bool {
for _, v := range slice {
if predicate == nil || predicate(v) {
return true
}
}
return false
}

// Every returns if every item matches the specified predicate
func (slice Slice[T]) Every(predicate Predicate[T]) bool {
for _, v := range slice {
if !predicate(v) {
return false
}
}
return true
}

// Copy returns a copy of the slice
func (slice Slice[T]) Copy() Slice[T] {
rSlice := make(Slice[T], len(slice))
copy(rSlice, slice)
return rSlice
}

// Reverse returns the slice in a reversed order
func (slice Slice[T]) Reverse() Slice[T] {
rSlice := slice.Copy()
for i, j := 0, len(rSlice)-1; i < j; i, j = i+1, j-1 {
rSlice[i], rSlice[j] = rSlice[j], rSlice[i]
}
return rSlice
}

type User struct {
Name string
Tickets int
}

func main() {
slice := Slice[User]{
{Name: "a", Tickets: 3},
{Name: "b", Tickets: 1},
{Name: "c", Tickets: 2},
}
fmt.Printf("Users with >= 2 tickets: %v\n", slice.Filter(func (u User) bool{
return u.Tickets >= 2
}))
fmt.Printf("First with <= 2 tickets: %v\n", slice.First(func (u User) bool{
return u.Tickets <= 2
}))
fmt.Printf("Last with >= 3 tickets: %v\n", slice.Last(func (u User) bool{
return u.Tickets >= 3
}))
fmt.Printf("Last with >= 10 tickets: %v\n", slice.Last(func (u User) bool{
return u.Tickets >= 10
}))
fmt.Printf("Has any with name \"foo\": %v\n", slice.Any(func (u User) bool{
return u.Name == "foo"
}))
fmt.Printf("Every has > 0 tickets: %v\n", slice.Every(func (u User) bool{
return u.Tickets > 0
}))
fmt.Printf("Slice address: %p / Slice copy address: %p\n", slice, slice.Copy())
fmt.Printf("Slice in reverse: %v\n", slice.Reverse())
fmt.Printf("Original Slice: %v\n", slice)
}
schonmann marked this conversation as resolved.
Show resolved Hide resolved
73 changes: 73 additions & 0 deletions lib/slice_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package lib

import (
"reflect"
"testing"
)

type TestUser struct {
Name string
Age int
}

func TestSliceFilter(t *testing.T) {
type args struct {
users Slice[TestUser]
predicate Predicate[TestUser]
}
tests := []struct {
name string
args args
want Slice[TestUser]
}{
{
name: "should return [Jonah]",
args: args{
users: Slice[TestUser]{
{Name: "Jonah", Age: 18},
{Name: "Camile", Age: 22},
},
predicate: func(u TestUser) bool {
return u.Name == "Jonah"
},
},
want: Slice[TestUser]{
{Name: "Jonah", Age: 18},
},
},
{
name: "should return []",
args: args{
users: Slice[TestUser]{
{Name: "Jonah", Age: 18},
{Name: "Camile", Age: 22},
},
predicate: func(u TestUser) bool {
return u.Name == "Math"
},
},
want: Slice[TestUser]{},
},
{
name: "should return all if predicate is nil",
args: args{
users: Slice[TestUser]{
{Name: "Jonah", Age: 18},
{Name: "Camile", Age: 22},
},
predicate: nil,
},
want: Slice[TestUser]{
{Name: "Jonah", Age: 18},
{Name: "Camile", Age: 22},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if actual := tt.args.users.Filter(tt.args.predicate); !reflect.DeepEqual(tt.want, actual) {
t.Fail()
}
})
}
}