xref: /aosp_15_r20/external/licenseclassifier/internal/sets/stringset.go (revision 46c4c49da23cae783fa41bf46525a6505638499a)
1*46c4c49dSIbrahim Kanouche// Copyright 2017 Google Inc.
2*46c4c49dSIbrahim Kanouche//
3*46c4c49dSIbrahim Kanouche// Licensed under the Apache License, Version 2.0 (the "License");
4*46c4c49dSIbrahim Kanouche// you may not use this file except in compliance with the License.
5*46c4c49dSIbrahim Kanouche// You may obtain a copy of the License at
6*46c4c49dSIbrahim Kanouche//
7*46c4c49dSIbrahim Kanouche//	http://www.apache.org/licenses/LICENSE-2.0
8*46c4c49dSIbrahim Kanouche//
9*46c4c49dSIbrahim Kanouche// Unless required by applicable law or agreed to in writing, software
10*46c4c49dSIbrahim Kanouche// distributed under the License is distributed on an "AS IS" BASIS,
11*46c4c49dSIbrahim Kanouche// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*46c4c49dSIbrahim Kanouche// See the License for the specific language governing permissions and
13*46c4c49dSIbrahim Kanouche// limitations under the License.
14*46c4c49dSIbrahim Kanouchepackage sets
15*46c4c49dSIbrahim Kanouche
16*46c4c49dSIbrahim Kanoucheimport (
17*46c4c49dSIbrahim Kanouche	"fmt"
18*46c4c49dSIbrahim Kanouche	"sort"
19*46c4c49dSIbrahim Kanouche	"strings"
20*46c4c49dSIbrahim Kanouche)
21*46c4c49dSIbrahim Kanouche
22*46c4c49dSIbrahim Kanouche// StringSet stores a set of unique string elements.
23*46c4c49dSIbrahim Kanouchetype StringSet struct {
24*46c4c49dSIbrahim Kanouche	set map[string]present
25*46c4c49dSIbrahim Kanouche}
26*46c4c49dSIbrahim Kanouche
27*46c4c49dSIbrahim Kanouche// NewStringSet creates a StringSet containing the supplied initial string elements.
28*46c4c49dSIbrahim Kanouchefunc NewStringSet(elements ...string) *StringSet {
29*46c4c49dSIbrahim Kanouche	s := &StringSet{}
30*46c4c49dSIbrahim Kanouche	s.set = make(map[string]present)
31*46c4c49dSIbrahim Kanouche	s.Insert(elements...)
32*46c4c49dSIbrahim Kanouche	return s
33*46c4c49dSIbrahim Kanouche}
34*46c4c49dSIbrahim Kanouche
35*46c4c49dSIbrahim Kanouche// Copy returns a newly allocated copy of the supplied StringSet.
36*46c4c49dSIbrahim Kanouchefunc (s *StringSet) Copy() *StringSet {
37*46c4c49dSIbrahim Kanouche	c := NewStringSet()
38*46c4c49dSIbrahim Kanouche	if s != nil {
39*46c4c49dSIbrahim Kanouche		for e := range s.set {
40*46c4c49dSIbrahim Kanouche			c.set[e] = present{}
41*46c4c49dSIbrahim Kanouche		}
42*46c4c49dSIbrahim Kanouche	}
43*46c4c49dSIbrahim Kanouche	return c
44*46c4c49dSIbrahim Kanouche}
45*46c4c49dSIbrahim Kanouche
46*46c4c49dSIbrahim Kanouche// Insert zero or more string elements into the StringSet.
47*46c4c49dSIbrahim Kanouche// As expected for a Set, elements already present in the StringSet are
48*46c4c49dSIbrahim Kanouche// simply ignored.
49*46c4c49dSIbrahim Kanouchefunc (s *StringSet) Insert(elements ...string) {
50*46c4c49dSIbrahim Kanouche	for _, e := range elements {
51*46c4c49dSIbrahim Kanouche		s.set[e] = present{}
52*46c4c49dSIbrahim Kanouche	}
53*46c4c49dSIbrahim Kanouche}
54*46c4c49dSIbrahim Kanouche
55*46c4c49dSIbrahim Kanouche// Delete zero or more string elements from the StringSet.
56*46c4c49dSIbrahim Kanouche// Any elements not present in the StringSet are simply ignored.
57*46c4c49dSIbrahim Kanouchefunc (s *StringSet) Delete(elements ...string) {
58*46c4c49dSIbrahim Kanouche	for _, e := range elements {
59*46c4c49dSIbrahim Kanouche		delete(s.set, e)
60*46c4c49dSIbrahim Kanouche	}
61*46c4c49dSIbrahim Kanouche}
62*46c4c49dSIbrahim Kanouche
63*46c4c49dSIbrahim Kanouche// Intersect returns a new StringSet containing the intersection of the
64*46c4c49dSIbrahim Kanouche// receiver and argument StringSets.  Returns an empty set if the argument is nil.
65*46c4c49dSIbrahim Kanouchefunc (s *StringSet) Intersect(other *StringSet) *StringSet {
66*46c4c49dSIbrahim Kanouche	if other == nil {
67*46c4c49dSIbrahim Kanouche		return NewStringSet()
68*46c4c49dSIbrahim Kanouche	}
69*46c4c49dSIbrahim Kanouche
70*46c4c49dSIbrahim Kanouche	// Point a and b to the maps, setting a to the smaller of the two.
71*46c4c49dSIbrahim Kanouche	a, b := s.set, other.set
72*46c4c49dSIbrahim Kanouche	if len(b) < len(a) {
73*46c4c49dSIbrahim Kanouche		a, b = b, a
74*46c4c49dSIbrahim Kanouche	}
75*46c4c49dSIbrahim Kanouche
76*46c4c49dSIbrahim Kanouche	// Perform the intersection.
77*46c4c49dSIbrahim Kanouche	intersect := NewStringSet()
78*46c4c49dSIbrahim Kanouche	for e := range a {
79*46c4c49dSIbrahim Kanouche		if _, ok := b[e]; ok {
80*46c4c49dSIbrahim Kanouche			intersect.set[e] = present{}
81*46c4c49dSIbrahim Kanouche		}
82*46c4c49dSIbrahim Kanouche	}
83*46c4c49dSIbrahim Kanouche	return intersect
84*46c4c49dSIbrahim Kanouche}
85*46c4c49dSIbrahim Kanouche
86*46c4c49dSIbrahim Kanouche// Disjoint returns true if the intersection of the receiver and the argument
87*46c4c49dSIbrahim Kanouche// StringSets is the empty set.  Returns true if the argument is nil or either
88*46c4c49dSIbrahim Kanouche// StringSet is the empty set.
89*46c4c49dSIbrahim Kanouchefunc (s *StringSet) Disjoint(other *StringSet) bool {
90*46c4c49dSIbrahim Kanouche	if other == nil || len(other.set) == 0 || len(s.set) == 0 {
91*46c4c49dSIbrahim Kanouche		return true
92*46c4c49dSIbrahim Kanouche	}
93*46c4c49dSIbrahim Kanouche
94*46c4c49dSIbrahim Kanouche	// Point a and b to the maps, setting a to the smaller of the two.
95*46c4c49dSIbrahim Kanouche	a, b := s.set, other.set
96*46c4c49dSIbrahim Kanouche	if len(b) < len(a) {
97*46c4c49dSIbrahim Kanouche		a, b = b, a
98*46c4c49dSIbrahim Kanouche	}
99*46c4c49dSIbrahim Kanouche
100*46c4c49dSIbrahim Kanouche	// Check for non-empty intersection.
101*46c4c49dSIbrahim Kanouche	for e := range a {
102*46c4c49dSIbrahim Kanouche		if _, ok := b[e]; ok {
103*46c4c49dSIbrahim Kanouche			return false // Early-exit because intersecting.
104*46c4c49dSIbrahim Kanouche		}
105*46c4c49dSIbrahim Kanouche	}
106*46c4c49dSIbrahim Kanouche	return true
107*46c4c49dSIbrahim Kanouche}
108*46c4c49dSIbrahim Kanouche
109*46c4c49dSIbrahim Kanouche// Difference returns a new StringSet containing the elements in the receiver
110*46c4c49dSIbrahim Kanouche// that are not present in the argument StringSet. Returns a copy of the
111*46c4c49dSIbrahim Kanouche// receiver if the argument is nil.
112*46c4c49dSIbrahim Kanouchefunc (s *StringSet) Difference(other *StringSet) *StringSet {
113*46c4c49dSIbrahim Kanouche	if other == nil {
114*46c4c49dSIbrahim Kanouche		return s.Copy()
115*46c4c49dSIbrahim Kanouche	}
116*46c4c49dSIbrahim Kanouche
117*46c4c49dSIbrahim Kanouche	// Insert only the elements in the receiver that are not present in the
118*46c4c49dSIbrahim Kanouche	// argument StringSet.
119*46c4c49dSIbrahim Kanouche	diff := NewStringSet()
120*46c4c49dSIbrahim Kanouche	for e := range s.set {
121*46c4c49dSIbrahim Kanouche		if _, ok := other.set[e]; !ok {
122*46c4c49dSIbrahim Kanouche			diff.set[e] = present{}
123*46c4c49dSIbrahim Kanouche		}
124*46c4c49dSIbrahim Kanouche	}
125*46c4c49dSIbrahim Kanouche	return diff
126*46c4c49dSIbrahim Kanouche}
127*46c4c49dSIbrahim Kanouche
128*46c4c49dSIbrahim Kanouche// Unique returns a new StringSet containing the elements in the receiver
129*46c4c49dSIbrahim Kanouche// that are not present in the argument StringSet *and* the elements in the
130*46c4c49dSIbrahim Kanouche// argument StringSet that are not in the receiver (which is the union of two
131*46c4c49dSIbrahim Kanouche// disjoint sets). Returns a copy of the
132*46c4c49dSIbrahim Kanouche// receiver if the argument is nil.
133*46c4c49dSIbrahim Kanouchefunc (s *StringSet) Unique(other *StringSet) *StringSet {
134*46c4c49dSIbrahim Kanouche	if other == nil {
135*46c4c49dSIbrahim Kanouche		return s.Copy()
136*46c4c49dSIbrahim Kanouche	}
137*46c4c49dSIbrahim Kanouche
138*46c4c49dSIbrahim Kanouche	sNotInOther := s.Difference(other)
139*46c4c49dSIbrahim Kanouche	otherNotInS := other.Difference(s)
140*46c4c49dSIbrahim Kanouche
141*46c4c49dSIbrahim Kanouche	// Duplicate Union implementation here to avoid extra Copy, since both
142*46c4c49dSIbrahim Kanouche	// sNotInOther and otherNotInS are already copies.
143*46c4c49dSIbrahim Kanouche	unique := sNotInOther
144*46c4c49dSIbrahim Kanouche	for e := range otherNotInS.set {
145*46c4c49dSIbrahim Kanouche		unique.set[e] = present{}
146*46c4c49dSIbrahim Kanouche	}
147*46c4c49dSIbrahim Kanouche	return unique
148*46c4c49dSIbrahim Kanouche}
149*46c4c49dSIbrahim Kanouche
150*46c4c49dSIbrahim Kanouche// Equal returns true if the receiver and the argument StringSet contain
151*46c4c49dSIbrahim Kanouche// exactly the same elements.
152*46c4c49dSIbrahim Kanouchefunc (s *StringSet) Equal(other *StringSet) bool {
153*46c4c49dSIbrahim Kanouche	if s == nil || other == nil {
154*46c4c49dSIbrahim Kanouche		return s == nil && other == nil
155*46c4c49dSIbrahim Kanouche	}
156*46c4c49dSIbrahim Kanouche
157*46c4c49dSIbrahim Kanouche	// Two sets of different length cannot have the exact same unique elements.
158*46c4c49dSIbrahim Kanouche	if len(s.set) != len(other.set) {
159*46c4c49dSIbrahim Kanouche		return false
160*46c4c49dSIbrahim Kanouche	}
161*46c4c49dSIbrahim Kanouche
162*46c4c49dSIbrahim Kanouche	// Only one loop is needed. If the two sets are known to be of equal
163*46c4c49dSIbrahim Kanouche	// length, then the two sets are equal only if exactly all of the elements
164*46c4c49dSIbrahim Kanouche	// in the first set are found in the second.
165*46c4c49dSIbrahim Kanouche	for e := range s.set {
166*46c4c49dSIbrahim Kanouche		if _, ok := other.set[e]; !ok {
167*46c4c49dSIbrahim Kanouche			return false
168*46c4c49dSIbrahim Kanouche		}
169*46c4c49dSIbrahim Kanouche	}
170*46c4c49dSIbrahim Kanouche
171*46c4c49dSIbrahim Kanouche	return true
172*46c4c49dSIbrahim Kanouche}
173*46c4c49dSIbrahim Kanouche
174*46c4c49dSIbrahim Kanouche// Union returns a new StringSet containing the union of the receiver and
175*46c4c49dSIbrahim Kanouche// argument StringSets.  Returns a copy of the receiver if the argument is nil.
176*46c4c49dSIbrahim Kanouchefunc (s *StringSet) Union(other *StringSet) *StringSet {
177*46c4c49dSIbrahim Kanouche	union := s.Copy()
178*46c4c49dSIbrahim Kanouche	if other != nil {
179*46c4c49dSIbrahim Kanouche		for e := range other.set {
180*46c4c49dSIbrahim Kanouche			union.set[e] = present{}
181*46c4c49dSIbrahim Kanouche		}
182*46c4c49dSIbrahim Kanouche	}
183*46c4c49dSIbrahim Kanouche	return union
184*46c4c49dSIbrahim Kanouche}
185*46c4c49dSIbrahim Kanouche
186*46c4c49dSIbrahim Kanouche// Contains returns true if element is in the StringSet.
187*46c4c49dSIbrahim Kanouchefunc (s *StringSet) Contains(element string) bool {
188*46c4c49dSIbrahim Kanouche	_, in := s.set[element]
189*46c4c49dSIbrahim Kanouche	return in
190*46c4c49dSIbrahim Kanouche}
191*46c4c49dSIbrahim Kanouche
192*46c4c49dSIbrahim Kanouche// Len returns the number of unique elements in the StringSet.
193*46c4c49dSIbrahim Kanouchefunc (s *StringSet) Len() int {
194*46c4c49dSIbrahim Kanouche	return len(s.set)
195*46c4c49dSIbrahim Kanouche}
196*46c4c49dSIbrahim Kanouche
197*46c4c49dSIbrahim Kanouche// Empty returns true if the receiver is the empty set.
198*46c4c49dSIbrahim Kanouchefunc (s *StringSet) Empty() bool {
199*46c4c49dSIbrahim Kanouche	return len(s.set) == 0
200*46c4c49dSIbrahim Kanouche}
201*46c4c49dSIbrahim Kanouche
202*46c4c49dSIbrahim Kanouche// Elements returns a []string of the elements in the StringSet, in no
203*46c4c49dSIbrahim Kanouche// particular (or consistent) order.
204*46c4c49dSIbrahim Kanouchefunc (s *StringSet) Elements() []string {
205*46c4c49dSIbrahim Kanouche	elements := []string{} // Return at least an empty slice rather than nil.
206*46c4c49dSIbrahim Kanouche	for e := range s.set {
207*46c4c49dSIbrahim Kanouche		elements = append(elements, e)
208*46c4c49dSIbrahim Kanouche	}
209*46c4c49dSIbrahim Kanouche	return elements
210*46c4c49dSIbrahim Kanouche}
211*46c4c49dSIbrahim Kanouche
212*46c4c49dSIbrahim Kanouche// Sorted returns a sorted []string of the elements in the StringSet.
213*46c4c49dSIbrahim Kanouchefunc (s *StringSet) Sorted() []string {
214*46c4c49dSIbrahim Kanouche	elements := s.Elements()
215*46c4c49dSIbrahim Kanouche	sort.Strings(elements)
216*46c4c49dSIbrahim Kanouche	return elements
217*46c4c49dSIbrahim Kanouche}
218*46c4c49dSIbrahim Kanouche
219*46c4c49dSIbrahim Kanouche// String formats the StringSet elements as sorted strings, representing them
220*46c4c49dSIbrahim Kanouche// in "array initializer" syntax.
221*46c4c49dSIbrahim Kanouchefunc (s *StringSet) String() string {
222*46c4c49dSIbrahim Kanouche	elements := s.Sorted()
223*46c4c49dSIbrahim Kanouche	var quoted []string
224*46c4c49dSIbrahim Kanouche	for _, e := range elements {
225*46c4c49dSIbrahim Kanouche		quoted = append(quoted, fmt.Sprintf("%q", e))
226*46c4c49dSIbrahim Kanouche	}
227*46c4c49dSIbrahim Kanouche	return fmt.Sprintf("{%s}", strings.Join(quoted, ", "))
228*46c4c49dSIbrahim Kanouche}
229