Skip to content
/ pie Public
forked from elliotchance/pie

πŸ• Enjoy a slice! A utility library for dealing with slices and maps that focuses on type safety and performance.

License

Notifications You must be signed in to change notification settings

danielpsf/pie

Β 
Β 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

75 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ• github.com/elliotchance/pie

GoDoc Build Status codecov

Enjoy a slice! pie is a code generator for dealing with slices that focuses on type safety, performance and immutability.

Quick Start

Install/Update

go get -u github.com/elliotchance/pie

Built-in Types

pie ships with some slice types ready to go (pun intended). These include:

These can be used without needing go generate. For example:

package main

import (
    "fmt"
    "strings"

    "github.com/elliotchance/pie/pie"
)

func main() {
    name := pie.Strings{"Bob", "Sally", "John", "Jane"}.
        FilterNot(func (name string) bool {
            return strings.HasPrefix(name, "J")
        }).
        Map(strings.ToUpper).
        Last()

    fmt.Println(name) // "SALLY"
}

Custom Types

Annotate the slice type in your source code:

type Car struct {
    Name, Color string
}

//go:generate pie Cars.*
type Cars []Car

Run go generate. This will create a file called cars_pie.go. You should commit this with the rest of your code. Run go generate any time you need to add more types.

Now you can use the slices:

cars := Cars{
    {"Bob", "blue"},
    {"Sally", "green"},
    {"John", "red"},
    {"Jane", "red"},
}

redCars := cars.Filter(func(car Car) bool {
    return car.Color == "red"
})

// redCars = Cars{{"John", "red"}, {"Jane", "red"}}

Or, more complex operations can be chained:

cars.FilterNot(func (car Car) {
        return strings.HasPrefix(car.Name, "J")
    }).
    Map(func (car Car) Car {
        car.Name = strings.ToUpper(car.Name)

        return car
    }).
    Last()

// Car{"SALLY", "green"}

Custom Equality

Some functions that compare elements, such as Contains will use the following method if it is available on the element type:

func (a ElementType) Equals(b ElementType) bool

The ElementType must be the same for the receiver and argument and it must return a bool. Be careful to create the function on the pointer or non-pointer type that is used by the slice.

Here is a minimal example:

type Car struct {
	Name, Color string
}

type Cars []*Car // ElementType is *Car

func (c *Car) Equals(c2 *Car) bool {
	return c.Name == c2.Name
}

Custom Stringer

Some functions that need elements to be represented as strings, such as Strings() will try to use the fmt.Stringer interface. If it's not available then it will fallback to:

fmt.Sprintf("%v", element)

Limiting Functions Generated

The .* can be used to generate all functions. This is easy to get going but creates a lot of unused code. You can limit the functions generated by chaining the function names with a dot syntax, like:

//go:generate pie myInts.Average.Sum myStrings.Filter

This will only generate myInts.Average, myInts.Sum and myStrings.Filter.

Functions

Below is a summary of the available functions.

The letters in brackets indicate:

  • E: The function will use the Equals method if it is available. See Custom Equality.

  • S: The function will use the String method if it is available. See Custom Stringer.

Function String Number Struct Maps Big-O Description
Abs βœ“ n Abs will return the absolute value of all values in the slice.
All βœ“ βœ“ βœ“ n All will return true if all callbacks return true. If the list is empty then true is always returned.
Any βœ“ βœ“ βœ“ n Any will return true if any callbacks return true. If the list is empty then false is always returned.
Append βœ“ βœ“ βœ“ n A new slice with the elements appended to the end.
AreSorted βœ“ βœ“ n Check if the slice is already sorted.
AreUnique βœ“ βœ“ n Check if the slice contains only unique elements.
Average βœ“ n The average (mean) value, or a zeroed value.
Bottom βœ“ βœ“ βœ“ n Gets n elements from bottom.
Contains (E) βœ“ βœ“ βœ“ n Check if the value exists in the slice.
Diff (E) βœ“ βœ“ βœ“ nΒ² Diff returns the elements that needs to be added or removed from the first slice to have the same elements in the second slice.
DropTop βœ“ βœ“ βœ“ n A new slice after dropping the top n elements.
Each βœ“ βœ“ βœ“ n Perform an action on each element.
Extend βœ“ βœ“ βœ“ n A new slice with the elements from each slice appended to the end.
Filter βœ“ βœ“ βœ“ n A new slice containing only the elements that returned true from the condition.
FilterNot βœ“ βœ“ βœ“ n A new slice containing only the elements that returned false from the condition.
FindFirstUsing βœ“ βœ“ βœ“ n The index of the first element when the callback returns true or -1 if the callback does not return true, meaning that it did not match any element in the slice. It also can return -1 if the slice is empty or if it is nil.
First βœ“ βœ“ βœ“ 1 The first element, or a zeroed value.
FirstOr βœ“ βœ“ βœ“ 1 The first element, or a default value.
Float64s (S) βœ“ βœ“ βœ“ n Transforms each element into a float64.
Intersect βœ“ βœ“ n Intersect returns elements which exists in all slices.
Ints (S) βœ“ βœ“ βœ“ n Transforms each element into an int.
Join βœ“ n A string from joining each of the elements.
JSONBytes βœ“ βœ“ βœ“ n The JSON encoded bytes.
JSONString βœ“ βœ“ βœ“ n The JSON encoded string.
Keys βœ“ n Returns all keys in the map (in random order).
Last βœ“ βœ“ βœ“ 1 The last element, or a zeroed value.
LastOr βœ“ βœ“ βœ“ 1 The last element, or a default value.
Len βœ“ βœ“ βœ“ 1 Number of elements.
Map βœ“ βœ“ βœ“ n A new slice where each element has been mapped (transformed).
Max βœ“ βœ“ n The maximum value, or a zeroes value.
Median βœ“ nβ‹…log(n) Median returns the value separating the higher half from the lower half of a data sample.
Min βœ“ βœ“ n The minimum value, or a zeroed value.
Product βœ“ n Product of all elements.
Random βœ“ βœ“ βœ“ 1 Select a random element, or a zeroed value if empty.
Reduce βœ“ βœ“ n Continously apply a function to the slice (left to right), reducing it to a single value.
Reverse βœ“ βœ“ βœ“ n Reverse elements.
Send βœ“ βœ“ βœ“ n Send all element to channel.
Sequence βœ“ n Generates sequence of numbers.
SequenceUsing βœ“ βœ“ βœ“ n Generates new sequence of elements in range using function which will be creator.
Shuffle βœ“ βœ“ βœ“ n Returns a new shuffled slice.
Sort βœ“ βœ“ nβ‹…log(n) Return a new sorted slice.
SortStableUsing βœ“ βœ“ nβ‹…log(n) Return a new sorted slice, using custom comparator, keeping the original order of equal elements.
SortUsing βœ“ βœ“ nβ‹…log(n) Return a new sorted slice, using custom comparator.
Strings (S) βœ“ βœ“ βœ“ n Transforms each element into a string.
Sum βœ“ n Sum (total) of all elements.
Top βœ“ βœ“ βœ“ n Gets several elements from top(head of slice).
ToStrings βœ“ βœ“ βœ“ n Transforms each element to a string.
Unique βœ“ βœ“ n Return a new slice with only unique elements.
Values βœ“ n Returns all values in the map (in random order).

FAQ

What are the requirements?

pie supports many Go versions, all the way back to Go 1.8.

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.

  4. Immutable. Functions never modify inputs, unlike some built-ins such as sort.Strings.

How do I contribute a function?

Pull requests are always welcome.

Here is a comprehensive list of steps to follow to add a new function:

  1. Create a new file in the functions/ directory. The file should be named the same as the function. You must include documentation for your function.

  2. Update functions/main.go to register the new function by adding an entry to Functions. Make sure you choose the correct For value that is appropriate for your function.

  3. Run go generate ./... && go install && go generate ./.... The first generate is to create the pie templates, install will update your binary for the annotations and the second generate will use the newly created templates to update the generated code for the internal types. If you encounter errors with your code you can safely rerun the command above.

  4. If you chose ForAll or ForStructs, then you must add unit tests to pie/carpointers_test.go and pie/cars_test.go.

  5. If you chose ForAll, ForNumbersAndStrings or ForNumbers, then you must add unit tests to pie/float64s_test.go and pie/ints_test.go.

  6. If you chose ForAll or ForStrings, then you must add unit tests to pie/strings_test.go.

  7. If you chose ForMaps, then you must add unit tests to pie/currencies.go.

  8. Update the README to list the new functions.

Why is the emoji a slice of pizza instead of a pie?

I wanted to pick a name for the project that was short and had an associated emoji. I liked pie, but then I found out that the pie emoji is not fully supported everywhere. I didn't want to change the name of the project to cake, but pizza pie still made sense. I'm not sure if I will change it back to a pie later.

About

πŸ• Enjoy a slice! A utility library for dealing with slices and maps that focuses on type safety and performance.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Go 100.0%