summaryrefslogtreecommitdiffstats
path: root/modules/container
diff options
context:
space:
mode:
Diffstat (limited to 'modules/container')
-rw-r--r--modules/container/filter.go21
-rw-r--r--modules/container/filter_test.go28
-rw-r--r--modules/container/set.go56
-rw-r--r--modules/container/set_test.go36
4 files changed, 141 insertions, 0 deletions
diff --git a/modules/container/filter.go b/modules/container/filter.go
new file mode 100644
index 0000000..37ec7c3
--- /dev/null
+++ b/modules/container/filter.go
@@ -0,0 +1,21 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package container
+
+import "slices"
+
+// FilterSlice ranges over the slice and calls include() for each element.
+// If the second returned value is true, the first returned value will be included in the resulting
+// slice (after deduplication).
+func FilterSlice[E any, T comparable](s []E, include func(E) (T, bool)) []T {
+ filtered := make([]T, 0, len(s)) // slice will be clipped before returning
+ seen := make(map[T]bool, len(s))
+ for i := range s {
+ if v, ok := include(s[i]); ok && !seen[v] {
+ filtered = append(filtered, v)
+ seen[v] = true
+ }
+ }
+ return slices.Clip(filtered)
+}
diff --git a/modules/container/filter_test.go b/modules/container/filter_test.go
new file mode 100644
index 0000000..ad304e5
--- /dev/null
+++ b/modules/container/filter_test.go
@@ -0,0 +1,28 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package container
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestFilterMapUnique(t *testing.T) {
+ result := FilterSlice([]int{
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ }, func(i int) (int, bool) {
+ switch i {
+ case 0:
+ return 0, true // included later
+ case 1:
+ return 0, true // duplicate of previous (should be ignored)
+ case 2:
+ return 2, false // not included
+ default:
+ return i, true
+ }
+ })
+ assert.Equal(t, []int{0, 3, 4, 5, 6, 7, 8, 9}, result)
+}
diff --git a/modules/container/set.go b/modules/container/set.go
new file mode 100644
index 0000000..1577998
--- /dev/null
+++ b/modules/container/set.go
@@ -0,0 +1,56 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package container
+
+type Set[T comparable] map[T]struct{}
+
+// SetOf creates a set and adds the specified elements to it.
+func SetOf[T comparable](values ...T) Set[T] {
+ s := make(Set[T], len(values))
+ s.AddMultiple(values...)
+ return s
+}
+
+// Add adds the specified element to a set.
+// Returns true if the element is added; false if the element is already present.
+func (s Set[T]) Add(value T) bool {
+ if _, has := s[value]; !has {
+ s[value] = struct{}{}
+ return true
+ }
+ return false
+}
+
+// AddMultiple adds the specified elements to a set.
+func (s Set[T]) AddMultiple(values ...T) {
+ for _, value := range values {
+ s.Add(value)
+ }
+}
+
+// Contains determines whether a set contains the specified element.
+// Returns true if the set contains the specified element; otherwise, false.
+func (s Set[T]) Contains(value T) bool {
+ _, has := s[value]
+ return has
+}
+
+// Remove removes the specified element.
+// Returns true if the element is successfully found and removed; otherwise, false.
+func (s Set[T]) Remove(value T) bool {
+ if _, has := s[value]; has {
+ delete(s, value)
+ return true
+ }
+ return false
+}
+
+// Values gets a list of all elements in the set.
+func (s Set[T]) Values() []T {
+ keys := make([]T, 0, len(s))
+ for k := range s {
+ keys = append(keys, k)
+ }
+ return keys
+}
diff --git a/modules/container/set_test.go b/modules/container/set_test.go
new file mode 100644
index 0000000..1502236
--- /dev/null
+++ b/modules/container/set_test.go
@@ -0,0 +1,36 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package container
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestSet(t *testing.T) {
+ s := make(Set[string])
+
+ assert.True(t, s.Add("key1"))
+ assert.False(t, s.Add("key1"))
+ assert.True(t, s.Add("key2"))
+
+ assert.True(t, s.Contains("key1"))
+ assert.True(t, s.Contains("key2"))
+ assert.False(t, s.Contains("key3"))
+
+ assert.True(t, s.Remove("key2"))
+ assert.False(t, s.Contains("key2"))
+
+ assert.False(t, s.Remove("key3"))
+
+ s.AddMultiple("key4", "key5")
+ assert.True(t, s.Contains("key4"))
+ assert.True(t, s.Contains("key5"))
+
+ s = SetOf("key6", "key7")
+ assert.False(t, s.Contains("key1"))
+ assert.True(t, s.Contains("key6"))
+ assert.True(t, s.Contains("key7"))
+}