gollections

package module
v0.0.0-...-7b9ace6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 23, 2014 License: MIT Imports: 4 Imported by: 0

README

"Generic" Go(Golang) collections

The collections provides many powerful methods, some taking functions, making for productive programming.

Here is a small example covering only a few of the features:

    s := NewSlice()
    s.Append("_")
    s.AppendAll("A", "B", "Z", "J")
    var val string
    s.Get(2, &val)             // Extract the element at index 2 (Z) into val (string)
    s.Get(-3, &val)            // Extract the third to last element (B) into val
    log.Print(s.ContainsAny("K", "Z")) // true
    s.Clear().AppendAll(1,2,3,4,5,6)   // can chain most calls
    // Example of calculating the sum using Reduce()
    sum := s.Reduce(0, func(reduction interface{}, i int, elem interface{}) interface{} {
      return reduction.(int) + elem.(int)
    })
    s.Reverse()                        // Reverse (in place)
    log.Print(s.Join(","))             // "6,5,4,3,2,1"
    s.Pop(&val)                        // Pop the last element (1) into val

What does it do

It provides "generic" Slice and Map elements for go. They are feature full and mostly modeled against the Fantom List and Map implementations.

Docs & Examples

Gollections has some detailed Godocs:

http://godoc.org/github.com/tcolar/gollections

You will find some examples here :

http://godoc.org/github.com/tcolar/gollections#example-Slice

Even more details can be found in the detailed unit tests:

https://github.com/tcolar/gollections/blob/master/slice_test.go

Installing

go get github.com/tcolar/gollections

Why ?

I'm actually not much of a generics lover, I never liked the way they where implemented in Java for example.

I've used Fantom a lot and while it has no generics either it provides very powerful collections that make you rarely miss them.

On the other Hand Go has neither generics nor collections with a lot of features, so this is an attempt to fill that gap.

How does it work

The custom collections rely on slice of "generic" elements ([]interface{} in Go). Obviously that means that we lose some type safety, however it is mitigated a bit by the fact that you can retrieve elements into a strongly typed variable pointer.

For example

    s := NewSlice()
    s.AppendAll(5,6,7)
    var myInt int
    s.Get(1, &myint) // now myInt is a strongly typed int with the value 6

A benefit of this "trick" is that we do regain some type safety since we are getting the value back into a strongly typed variable(int) that the compiler can watch for us from then on.

Performance

Overall the performance is obviousyly slower than a native slice, yet not terrible. It can be around 5x slower than a native slice, which is not negligeable, yet still faster than many other languages.

Getting values from the generic slice into a typed variable has an extra cost due to the use of reflection, however so far benchmarking indicates it's not unreasonable. (More becnhmarking TBD)

I did put extra attention trying to make all the slice operations as efficient as I could. Most operations are done in place unless otherwise noted and try not to allocate any unnecessay space.

One operation that is very costly is To() which "exports" the slice contents into a strongly typed slice (native go slice), that requires the use of reflection and copy of each elements one at a time. So it's best to not use it at all or only use it as the very last step once all operations are completed.

Obviously it would have been best if such collections/functions where "baked in" as they could leverage the builtin parametric types that are not unavailable in the user space.

Documentation

Overview

Package gollections provides some"Generic" collections for go. Those collections are feature rich with many useful methods.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func PtrToVal

func PtrToVal(ptr interface{}) reflect.Value

Get the reflect.Value of the element pointed to by ptr Will panic if tr is not a pointer

Types

type Map

type Map struct {
}

type Slice

type Slice struct {

	// Returns whether two items are equal
	// Default imlementation uses reflect.DeepEqual (==)
	Equals func(a, b interface{}) bool

	// Optional comparator function, must return 0 if a==b; -1 if a < b; 1 if a>b
	// **Nil by default**
	// **MUST** be defined for sorting to work.
	Compare func(a, b interface{}) int
	// contains filtered or unexported fields
}

Custom "Generic" (Sorta) slice Note: Satisfies sort.Interface so can use sort, search as long as Compare is implemented

Example

Some usage examples for gollection.Slice This does not demonstarte all the methods, see godoc and tests for more details

s := NewSlice()                 // Create a new slice
s.Append("_")                   // add something to it
s.AppendAll("A", "B", "Z", "J") // add several more things
log.Print(s)                    // Slice[5] [_ A B Z J]
s.Insert(1, "*")                // insert
log.Print(s)                    // Slice[6] [_ * A B Z J]
s.RemoveAt(1)                   // Remove '*'
i := s.IndexOf("A")             // Find the index of the (first) element equal to "A" (1)
log.Print(i)                    // 1

var val string                  // We will get an element of the slice into this strongly typed var
s.Get(2, &val)                  // set 'val' to the value of slice element at index 2
log.Print(strings.ToLower(val)) // b
s.Last(&val)                    // Get the last element
log.Print(val)                  // J
s.Get(-2, &val)                 // Get "secnd to last" element
log.Print(val)                  // Z

log.Print(s.ContainsAny("K", "Z")) // Does s contain either K or Z ? -> true

log.Print(s.Join("|")) // "_|A|B|Z|J"

// Using Each() closure to manually create a string of the elements joined by '-'
val = ""
s.Each(func(i int, e interface{}) bool {
	val = fmt.Sprintf("%s-%s", val, e.(string))
	return false // No "stop" condition is this closure
})
log.Print(val) // _-A-B-Z-J

// More complex Each() form, iterating over a range with a stop condition
val = ""
s.EachRange(1, -2, func(i int, e interface{}) bool { // skip first and last elements
	val = fmt.Sprintf("%s-%s", val, e.(string))
	return e == "B" // But stop if we encountered a B
})
log.Print(val) // -A-B (We iterated from 'A' to 'Z' but stopped iterating after 'B')

// Example: using Any() to see if at least one element satisfies a condition
any := s.Any(func(e interface{}) bool {
	str := e.(string) // we are working on strings, so doing an assertion
	// Is the string the same in upper and lower case ?
	return strings.ToLower(str) == str
})
log.Print(any) // true because '_' is the same in upper and lower case

// Copying some of the slice content back into a strongly typed slice
// Note that it's a costly operation as all elements have to be copied individually
var raw []string
s.ToRange(1, -2, &raw) // retrieving all but first and last element
log.Print(raw)         // [A B Z]  ("standard" string slice)

// Using findAll function to create a new list
found := s.FindAll(func(i int, e interface{}) bool {
	return e.(string) < "X"
})
log.Print(found) // Slice[3] [A B J]

// see tests for examples of sort, search, map, reduce and more
Output:

func NewSlice

func NewSlice() *Slice

Initialize a new empty slice

func (*Slice) All

func (s *Slice) All(f func(interface{}) bool) bool

Return true if f returns true for all of the items in the list.

func (*Slice) Any

func (s *Slice) Any(f func(interface{}) bool) bool

Return true if c returns true for any(at least 1) of the items in the list

func (*Slice) Append

func (s *Slice) Append(elem interface{}) *Slice

Append a single value (in place) Return the slice pointer to allow method chaining.

func (*Slice) AppendAll

func (s *Slice) AppendAll(elems ...interface{}) *Slice

Append several values (in place) Return the slice pointer to allow method chaining.

func (*Slice) AppendSlice

func (s *Slice) AppendSlice(slice *Slice) *Slice

Append another Slice to this slice Return the slice pointer to allow method chaining.

func (*Slice) Cap

func (s *Slice) Cap() int

Current slice capacity

func (*Slice) Clear

func (s *Slice) Clear() *Slice

Clear (empty) the list Return the slice pointer to allow method chaining.

func (*Slice) Clone

func (s *Slice) Clone() *Slice

Create and return a clone of this slice

func (*Slice) CloneRange

func (s *Slice) CloneRange(from, to int) *Slice

Clone part of this slice into a new Slice From and To are both inclusive

func (*Slice) Contains

func (s *Slice) Contains(elem interface{}) bool

Does the slice contain the given element (by equality) Note, this uses simple iteration, use sort methods if needing more performance

func (*Slice) ContainsAll

func (s *Slice) ContainsAll(elems ...interface{}) bool

Does the slice contain all the given values

func (*Slice) ContainsAny

func (s *Slice) ContainsAny(elems ...interface{}) bool

Does the slice contain at least one of the given values

func (*Slice) Each

func (s *Slice) Each(f func(int, interface{}) (stop bool))

Apply the function to the whole slice (in order) If the function returns true (stop), iteration will stop

func (*Slice) EachRange

func (s *Slice) EachRange(from, to int, f func(int, interface{}) (stop bool))

Apply the function to the slice range From and To are both inclusive if from is < to it will iterate in reversed order If the function returns true (stop), iteration will stop

func (*Slice) Eachr

func (s *Slice) Eachr(f func(int, interface{}) (stop bool))

Apply the function to the whole slice (reverse order) If the function returns true (stop), iteration will stop

func (*Slice) Fill

func (s *Slice) Fill(elem interface{}, count int) *Slice

Fill(append to) the slice with 'count' times the 'elem' value (in place) Return the slice pointer to allow method chaining.

func (*Slice) Find

func (s *Slice) Find(f func(int, interface{}) (found bool)) (index int)

Apply a function to find an element in the slice (iteratively) Returns the index if found, or -1 if no matches. The function is expected to return true when the index is found.

func (*Slice) FindAll

func (s *Slice) FindAll(f func(int, interface{}) (found bool)) *Slice

Apply a function to find all element in the slice for which the function returns true Returns a new Slice made of the matches.

func (*Slice) First

func (s *Slice) First(ptr interface{})

Set value of ptr to this slice first element Return an error if slice is empty

func (*Slice) Get

func (s *Slice) Get(idx int, ptr interface{})

Set value of ptr to slice[idx] If idx is negative then idx element from the end -> slice[len(slice)+idx] ie Get(-1) would return the last element

func (*Slice) GetVal

func (s *Slice) GetVal(idx int, ptrVal reflect.Value)

Set value of ptr(Ptr is the Value of a pointer to the var to set) to slice[idx] When called repeatedly GetVal() can be ~30% faster than Get since it saves repeating the reflection call. Note: See PtrToVal()

func (*Slice) IndexOf

func (s *Slice) IndexOf(elem interface{}) int

Return the (lowest) index of given element (using Equals() method) Return -1 if the lement is part of the slice Note, this uses simple iteration, use sort methods if meeding more performance

func (*Slice) Insert

func (s *Slice) Insert(idx int, elem interface{}) *Slice

Insert the element before index idx Can use negative index Return the slice pointer to allow method chaining.

func (*Slice) InsertAll

func (s *Slice) InsertAll(idx int, elems ...interface{}) *Slice

Insert All the element before index idx Can use negative index Return the slice pointer to allow method chaining.

func (*Slice) InsertSlice

func (s *Slice) InsertSlice(idx int, slice *Slice) *Slice

Insert All the element of the slice before index idx Can use negative index Return the slice pointer to allow method chaining.

func (*Slice) IsEmpty

func (s *Slice) IsEmpty() bool

Is this slice empty

func (*Slice) Join

func (s *Slice) Join(sep string) string

Create a string by jining all the elements with the given seprator Note: Use fmt.Sprintf("%v", e) to get each element as a string

func (*Slice) Last

func (s *Slice) Last(ptr interface{})

Set value of ptr to this slice last element Will panic if slice is empty

func (*Slice) Len

func (s *Slice) Len() int

Length of this slice Also used for impl of sort.Interface

func (*Slice) Less

func (s *Slice) Less(a, b int) bool

Check if element at index a < b (used as impl of sort.Interface) S.Compare must be defined !

func (*Slice) Max

func (s *Slice) Max(ptr interface{})

Set ptr to the maximum value in the slice (pamic if slice is empty) NOTE: Compare function **MUST** be implemented This uses simple iteration (0n time) and does not modify the slice Alternatively use sort.Sort(slice).Get(-1, ptr) when performance is needed

func (*Slice) Min

func (s *Slice) Min(ptr interface{})

Set ptr to the minimum value in the slice (pamic if slice is empty) NOTE: Compare function **MUST** be implemented This uses simple iteration (0n time) and does not modify the slice Alternatively use sort.Sort(slice).Get(0, ptr) when performance is needed

func (*Slice) Peek

func (s *Slice) Peek(ptr interface{})

Set ptr to the last element Will panic if slice is empty

func (*Slice) Pop

func (s *Slice) Pop(ptr interface{})

Pop (return & remove) and set ptr to the last element Will panic if slice is empty

func (*Slice) Push

func (s *Slice) Push(elem interface{})

Push an elem at the end of the slice (same as Append)

func (*Slice) Reduce

func (s *Slice) Reduce(startVal interface{}, f func(reduction interface{}, index int, elem interface{}) interface{}) interface{}

Reduce is used to iterate through every item in the list to reduce the list into a single value called the reduction. The initial value (startVal) of the reduction is passed in as the init parameter then passed to the closure along with each item (which returns the updated reduction) See Tests / Examples in the test file for more info

func (*Slice) RemoveAt

func (s *Slice) RemoveAt(idx int) *Slice

Remove the element at the given index (in place) Return the slice pointer to allow method chaining.

func (*Slice) RemoveElem

func (s *Slice) RemoveElem(elem interface{}) *Slice

Remove, in place, the first element found by value equality (found by IndexFrom method) Return the slice pointer to allow method chaining.

func (*Slice) RemoveElems

func (s *Slice) RemoveElems(elem interface{}) *Slice

Remove, in place, all elements by value equality (using Equals function) Return the slice pointer to allow method chaining.

func (*Slice) RemoveFunc

func (s *Slice) RemoveFunc(f func(idx int, elem interface{}) bool) *Slice

Remove, in place, the elements that match the function (where the function return true)

func (*Slice) RemoveRange

func (s *Slice) RemoveRange(from, to int) *Slice

Remove the elements within the given index range Return the slice pointer to allow method chaining.

func (*Slice) Reverse

func (s *Slice) Reverse() *Slice

Reverse in place, the slice in place (first element becomes last etc...) Return the slice pointer to allow method chaining.

func (*Slice) Set

func (s *Slice) Set(idx int, elem interface{}) *Slice

Set the element at the given index Return the slice pointer to allow method chaining.

func (*Slice) Slice

func (s *Slice) Slice() *[]interface{}

Returns pointer to the raw underlying slice ([]interface{})

func (*Slice) String

func (s *Slice) String() string

impl String interface

func (*Slice) Swap

func (s *Slice) Swap(a, b int)

Swap 2 elements (used as impl of sort.Interface) Panics if the indexes are out of bounds

func (*Slice) To

func (s *Slice) To(ptr interface{})

Export our "generic" slice to a typed slice (say []int) Ptr needs to be a pointer to a slice Note that it can't be a simple cast and instead the data needs to be copied so it's definitely a VERY costly operation.

func (*Slice) ToRange

func (s *Slice) ToRange(from, to int, ptr interface{})

Same as To() but only get a subset(range) of the slice From and To are both inclusive Note that from and to can use negative index to indicate "from the end"

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL