diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fa2847b --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out +/.idea diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7cf60a3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Elliot Chance + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4828997 --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +# 🥧 `github.com/elliotchance/pie` + +**Enjoy a slice of pie!** `pie` is a utility library for dealing with slices +that focuses only on type safety and performance: + +```go +namesStartingWithJ := pie.Strings{"Bob", "Sally", "John", "Jane"}. + If(func(s string) bool { + return s[0] == 'J' + }) +``` + +See the [go docs](https://godoc.org/github.com/elliotchance/pie) for full API. + +# FAQ + +## How do I use it? + +You can include it like any other package through your favourite package +manager: + +1. Go modules ([you should be using this one](http://elliot.land/post/migrating-projects-from-dep-to-go-modules)): +`go get -u github.com/elliotchance/pie` + +2. Dep: `dep ensure -add github.com/elliotchance/pie` + +## Why do we need another library for this? + +Yes, there are some other great options like +[`thoas/go-funk`](https://github.com/thoas/go-funk), +[`leaanthony/slicer`](https://github.com/leaanthony/slicer), +[`viant/toolbox`](https://github.com/viant/toolbox) and +[`alxrm/ugo`](https://github.com/alxrm/ugo) to name a few. + +A lot of my work is dealing with servers that need to be high performance. I +found myself creating all the same utility functions like StringSliceContains +because I wanted to avoid reflection. + +## What are the goals of `pie`? + +1. **Type safety.** I never want to hit runtime bugs because I could pass in the +wrong type, or perform an invalid type case out the other end. + +2. **Performance.** The functions need to be as fast as native Go +implementations otherwise there's no point in this library existing. + +3. **Nil-safe.** All of the functions will happily accept nil and treat them as +empty slices. Apart from less possible panics, it makes it easier to chain. + +There are some downsides with this approach: + +1. It won't support all slice types. Sorry, you can use these actions on +`[]Foo`. + +2. Until +[parametric polymorphism (generics) possibly arrives in Go v2](https://go.googlesource.com/proposal/+/master/design/go2draft-generics-overview.md) +there will need to be duplicate code in `pie` to compensate. + +## Can I contribute? + +Absolutely. Pull requests are always welcome. diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..545688c --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module github.com/elliotchance/pie + +go 1.12 + +require github.com/stretchr/testify v1.3.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..4347755 --- /dev/null +++ b/go.sum @@ -0,0 +1,7 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= diff --git a/strings.go b/strings.go new file mode 100644 index 0000000..26acfc6 --- /dev/null +++ b/strings.go @@ -0,0 +1,46 @@ +// Package pie is a utility library for dealing with slices that focuses only on +// type safety and performance: +// +// namesStartingWithJ := pie.Strings{"Bob", "Sally", "John", "Jane"}. +// If(func(s string) bool { +// return s[0] == 'J' +// }) +// +package pie + +// Strings is an alias for a string slice. +// +// You can create a Strings directly: +// +// pie.Strings{"foo", "bar"} +// +// Or, cast an existing string slice: +// +// ss := []string{"foo", "bar"} +// pie.Strings(ss) +// +type Strings []string + +// Contains returns true if the string exists in the slice. The strings must be +// exactly equal (case-sensitive). +func (ss Strings) Contains(lookingFor string) bool { + for _, s := range ss { + if s == lookingFor { + return true + } + } + + return false +} + +// If will return a new slice containing only the elements that return true from +// the condition. The returned slice may contain zero elements (nil). +func (ss Strings) If(condition func(s string) bool) (ss2 Strings) { + for _, s := range ss { + if condition(s) { + ss2 = append(ss2, s) + } + } + + return +} diff --git a/strings_test.go b/strings_test.go new file mode 100644 index 0000000..3b0d048 --- /dev/null +++ b/strings_test.go @@ -0,0 +1,56 @@ +package pie_test + +import ( + "github.com/elliotchance/pie" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestStrings_Contains(t *testing.T) { + for _, test := range []struct { + ss pie.Strings + contains string + expected bool + }{ + {nil, "a", false}, + {nil, "", false}, + {pie.Strings{"a", "b", "c"}, "a", true}, + {pie.Strings{"a", "b", "c"}, "b", true}, + {pie.Strings{"a", "b", "c"}, "c", true}, + {pie.Strings{"a", "b", "c"}, "A", false}, + {pie.Strings{"a", "b", "c"}, "", false}, + {pie.Strings{"a", "b", "c"}, "d", false}, + {pie.Strings{"a", "", "c"}, "", true}, + } { + t.Run("", func(t *testing.T) { + assert.Equal(t, test.expected, test.ss.Contains(test.contains)) + }) + } +} + +func TestStrings_If(t *testing.T) { + for _, test := range []struct { + ss pie.Strings + condition func(string) bool + expected pie.Strings + }{ + { + nil, + func(s string) bool { + return s == "" + }, + nil, + }, + { + pie.Strings{"a", "b", "c"}, + func(s string) bool { + return s != "b" + }, + pie.Strings{"a", "c"}, + }, + } { + t.Run("", func(t *testing.T) { + assert.Equal(t, test.expected, test.ss.If(test.condition)) + }) + } +}