// Package set provides both threadsafe and non-threadsafe implementations of // a generic set data structure. In the threadsafe set, safety encompasses all // operations on one set. Operations on multiple sets are consistent in that // the elements of each set used was valid at exactly one point in time // between the start and the end of the operation. package set // Interface is describing a Set. Sets are an unordered, unique list of values. type Interface interface { New(items ...interface{}) Interface Add(items ...interface{}) Remove(items ...interface{}) Pop() interface{} Has(items ...interface{}) bool Size() int Clear() IsEmpty() bool IsEqual(s Interface) bool IsSubset(s Interface) bool IsSuperset(s Interface) bool Each(func(interface{}) bool) String() string List() []interface{} Copy() Interface Merge(s Interface) Separate(s Interface) } // helpful to not write everywhere struct{}{} var keyExists = struct{}{} // Union is the merger of multiple sets. It returns a new set with all the // elements present in all the sets that are passed. // // The dynamic type of the returned set is determined by the first passed set's // implementation of the New() method. func Union(set1, set2 Interface, sets ...Interface) Interface { u := set1.Copy() set2.Each(func(item interface{}) bool { u.Add(item) return true }) for _, set := range sets { set.Each(func(item interface{}) bool { u.Add(item) return true }) } return u } // Difference returns a new set which contains items which are in in the first // set but not in the others. Unlike the Difference() method you can use this // function separately with multiple sets. func Difference(set1, set2 Interface, sets ...Interface) Interface { s := set1.Copy() s.Separate(set2) for _, set := range sets { s.Separate(set) // seperate is thread safe } return s } // Intersection returns a new set which contains items that only exist in all given sets. func Intersection(set1, set2 Interface, sets ...Interface) Interface { all := Union(set1, set2, sets...) result := Union(set1, set2, sets...) all.Each(func(item interface{}) bool { if !set1.Has(item) || !set2.Has(item) { result.Remove(item) } for _, set := range sets { if !set.Has(item) { result.Remove(item) } } return true }) return result } // SymmetricDifference returns a new set which s is the difference of items which are in // one of either, but not in both. func SymmetricDifference(s Interface, t Interface) Interface { u := Difference(s, t) v := Difference(t, s) return Union(u, v) } // StringSlice is a helper function that returns a slice of strings of s. If // the set contains mixed types of items only items of type string are returned. func StringSlice(s Interface) []string { slice := make([]string, 0) for _, item := range s.List() { v, ok := item.(string) if !ok { continue } slice = append(slice, v) } return slice } // IntSlice is a helper function that returns a slice of ints of s. If // the set contains mixed types of items only items of type int are returned. func IntSlice(s Interface) []int { slice := make([]int, 0) for _, item := range s.List() { v, ok := item.(int) if !ok { continue } slice = append(slice, v) } return slice }