Skip to content

Commit

Permalink
chore: adding All, Any, None, sets.FlipMap, maps.FlipSlice
Browse files Browse the repository at this point in the history
  • Loading branch information
zostay committed Sep 3, 2024
1 parent 605e577 commit 2e42102
Show file tree
Hide file tree
Showing 7 changed files with 227 additions and 9 deletions.
6 changes: 6 additions & 0 deletions Changes.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## WIP TBD

* Adding `maps.Flip`, `maps.FlipSlice`, and `sets.FlipMap`.
* Adding `sets.All`, `sets.Any`, and `sets.None`.
* The signature of `sets.Delete` and `sets.Insert` has changed to use variadic args to allow multiple deletes and inserts in a single call, respectively.

## v0.8.0 2024-06-25

* Adding `bytes.ContainsOnly`, `bytes.FromRange`, `bytes.Reverse`, `bytes.Indent`.
Expand Down
25 changes: 25 additions & 0 deletions maps/transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,35 @@ func KVs[K comparable, V any](in map[K]V) []any {
// Flip will return a new map with the keys and values of the input map flipped.
// That is, the keys of the input map will be the values of the output map and
// the values of the input map will be the keys of the output map.
//
// This will only work if the value type is comparable and if values are
// repeated, it is indeterminate which key will be kept.
func Flip[K comparable, V comparable](in map[K]V) map[V]K {
out := make(map[V]K, len(in))
for k, v := range in {
out[v] = k
}
return out
}

// FlipSlice will return a new map with the keys and values of the input map
// flipped. That is, the keys of the input map will be the values of the output
// map and the values of the input map will be the keys of the output map.
// However, the resulting value is a slice of keys.
//
// This will only work if the value type is comparable. If values repeat, the
// keys will be collected into the value slice. The ordering is indeterminate.
//
// You may also be interested in set.FlipMap.
func FlipSlice[K comparable, V comparable](in map[K]V) map[V][]K {
out := make(map[V][]K, len(in))
for k, v := range in {
outv, alreadyExists := out[v]
if !alreadyExists {
outv = make([]K, 0, 1)
}
outv = append(outv, k)
out[v] = outv
}
return out
}
26 changes: 26 additions & 0 deletions maps/transform_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,29 @@ func TestFlip(t *testing.T) {
assert.Equal(t, k, a[v])
}
}

func TestFlipSlice(t *testing.T) {
t.Parallel()

a := map[string]int{
"a": 1,
"b": 2,
"c": 3,
"d": 1,
"e": 1,
}

vks := maps.FlipSlice(a)
assert.Len(t, vks, 3)
for k, v := range a {
assert.Contains(t, vks, v)
assert.Contains(t, vks[v], k)
}

for k, vs := range vks {
for _, v := range vs {
assert.Contains(t, a, v)
assert.Equal(t, k, a[v])
}
}
}
57 changes: 53 additions & 4 deletions set/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,65 @@ func (s Set[T]) Contains(val T) bool {
return hasVal
}

// All returns true if all the given values are contained within the set.
// This is similar to running:
//
// s.Intersection(set.New(vals...)).Len()==len(vals)
//
// This is both more efficient and easier to read in many cases.
func (s Set[T]) All(vals ...T) bool {
for _, val := range vals {
if _, contains := s[val]; !contains {
return false
}
}
return true
}

// Any returns true if any of the given values are contained within the
// set. This is similar to running:
//
// s.Intersection(set.New(vals...)).Len()>0
//
// This is both more efficient and easier to read in many cases.
func (s Set[T]) Any(vals ...T) bool {
for _, val := range vals {
if _, contains := s[val]; contains {
return true
}
}
return false
}

// None returns true if none of the given values are contained within the
// set. This is similar to running:
//
// s.Intersection(set.New(vals...)).Len()==0
//
// This is both more efficient and easier to read in many cases.
func (s Set[T]) None(vals ...T) bool {
for _, val := range vals {
if _, contains := s[val]; contains {
return false
}
}
return true
}

// Insert adds the given value to the set. If the value is already present, this
// will have no effect.
func (s Set[T]) Insert(val T) {
s[val] = exists
func (s Set[T]) Insert(vals ...T) {
for _, val := range vals {
s[val] = exists
}
}

// Delete removes the given value from the set. If the value is not present,
// this will have no effect.
func (s Set[T]) Delete(val T) {
delete(s, val)
func (s Set[T]) Delete(val ...T) {
for _, val := range val {
delete(s, val)
}
}

// Len returns the size of the set.
Expand Down
64 changes: 59 additions & 5 deletions set/set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,21 +61,24 @@ func TestSet_Contains(t *testing.T) {
func TestSet_Delete(t *testing.T) {
t.Parallel()

s := set.New(1, 2, 3)
s := set.New(1, 2, 3, 4, 5, 6)

s.Delete(0)
assert.Equal(t, set.New(1, 2, 3), s)
assert.Equal(t, set.New(1, 2, 3, 4, 5, 6), s)

s.Delete(1)
assert.Equal(t, set.New(2, 3), s)
assert.Equal(t, set.New(2, 3, 4, 5, 6), s)

s.Delete(2)
assert.Equal(t, set.New(3), s)
assert.Equal(t, set.New(3, 4, 5, 6), s)

s.Delete(3)
assert.Equal(t, set.New(4, 5, 6), s)

s.Delete(4, 5, 6)
assert.Equal(t, set.New[int](), s)

s.Delete(4)
s.Delete(7)
assert.Equal(t, set.New[int](), s)
}

Expand All @@ -97,6 +100,9 @@ func TestSet_Insert(t *testing.T) {

s.Insert(3)
assert.Equal(t, set.New(3, 2, 1), s)

s.Insert(4, 5, 6)
assert.Equal(t, set.New(3, 2, 1, 4, 5, 6), s)
}

func TestSet_Len(t *testing.T) {
Expand Down Expand Up @@ -213,3 +219,51 @@ func TestDiff(t *testing.T) {
assert.Equal(t, set.New(2, 4), one)
assert.Equal(t, set.New(5, 7), two)
}

func TestSet_All(t *testing.T) {
t.Parallel()

s := set.New(1, 2, 3)

assert.True(t, s.All(1, 2, 3))
assert.True(t, s.All(1, 2))
assert.True(t, s.All(1))
assert.True(t, s.All(2))
assert.True(t, s.All(3))
assert.False(t, s.All(2, 3, 4))
assert.False(t, s.All(2, 4))
assert.False(t, s.All(4))
assert.False(t, s.All(4, 5, 6))
}

func TestSet_Any(t *testing.T) {
t.Parallel()

s := set.New(1, 2, 3)

assert.True(t, s.Any(1, 2, 3))
assert.True(t, s.Any(1, 2))
assert.True(t, s.Any(1))
assert.True(t, s.Any(2))
assert.True(t, s.Any(3))
assert.True(t, s.Any(2, 3, 4))
assert.True(t, s.Any(2, 4))
assert.False(t, s.Any(4))
assert.False(t, s.Any(4, 5, 6))
}

func TestSet_None(t *testing.T) {
t.Parallel()

s := set.New(1, 2, 3)

assert.False(t, s.None(1, 2, 3))
assert.False(t, s.None(1, 2))
assert.False(t, s.None(1))
assert.False(t, s.None(2))
assert.False(t, s.None(3))
assert.False(t, s.None(2, 3, 4))
assert.False(t, s.None(2, 4))
assert.True(t, s.None(4))
assert.True(t, s.None(4, 5, 6))
}
23 changes: 23 additions & 0 deletions set/transform.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package set

// FlipMap will return a new map with the keys and values of the input map
// flipped. That is, the keys of the input map will be the values of the output
// map and the values of the input map will be the keys of the output map.
// However, the resulting value is a Set of keys.
//
// This will only work if the value type is comparable. If values repeat, the
// keys will be collected into the value set. The ordering is indeterminate.
//
// You might also be interested in maps.Flip and maps.FlipSlice.
func FlipMap[K comparable, V comparable](in map[K]V) map[V]Set[K] {
out := make(map[V]Set[K], len(in))
for k, v := range in {
outv, alreadyExists := out[v]
if !alreadyExists {
outv = New[K]()
}
outv.Insert(k)
out[v] = outv
}
return out
}
35 changes: 35 additions & 0 deletions set/transform_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package set_test

import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/zostay/go-std/set"
)

func TestFlipMap(t *testing.T) {
t.Parallel()

a := map[string]int{
"a": 1,
"b": 2,
"c": 3,
"d": 1,
"e": 1,
}

vks := set.FlipMap(a)
assert.Len(t, vks, 3)
for k, v := range a {
assert.Contains(t, vks, v)
assert.True(t, vks[v].Contains(k))
}

for k, vs := range vks {
for _, v := range vs.Keys() {
assert.Contains(t, a, v)
assert.Equal(t, k, a[v])
}
}
}

0 comments on commit 2e42102

Please sign in to comment.