405

I'm trying to solve a Go language exercise which requires me to have a set. I can create a set type but why doesn't the language come with one?

Go, having come from Google, where Guava also originated, why didn't the language designers opt for adding support for fundamental data structures? Why force your users to create their own implementations for something so basic as a set?

8
  • 151
    hmmm. wondering why the negative votes ? I'm coming from the java world where we had a set almost right from the beginning, even without generics. So, I find this behavior a lil bizarre Commented Dec 2, 2015 at 5:02
  • 5
    Use this one: github.com/deckarep/golang-set Commented Jan 26, 2022 at 4:20
  • 2
    GoDS (Go Data Structures) looks promiting: github.com/emirpasic/Gods Commented Feb 26, 2023 at 10:21
  • 1
    Because of really poor decision making by the Go team. Other fundamental data structures which it provides (like container/list) are half-baked. Commented Dec 31, 2023 at 19:20
  • Many of the answers are outdated; Go 1.18 has already provided generics. Implementing it yourself is quite easy, though for completeness, you might want to add locks. However, I hope in the future Go can directly provide it in the standard library, saving the trouble of writing it yourself or referencing other libraries. Commented Apr 17, 2024 at 8:40

4 Answers 4

191

One reason is that it is easy to create a set from map:

s := map[int]bool{5: true, 2: true}
_, ok := s[6] // check for existence
s[8] = true // add element 
delete(s, 2) // remove element

Union

s_union := map[int]bool{}
for k, _ := range s1{
    s_union[k] = true
}
for k, _ := range s2{
    s_union[k] = true
}

Intersection

s_intersection := map[int]bool{}
if len(s1) > len(s2) {
  s1, s2 = s2, s1 // better to iterate over a shorter set
}
for k,_ := range s1 { 
  if s2[k] {
    s_intersection[k] = true
  }
}

It is not really that hard to implement all other set operations.

Sign up to request clarification or add additional context in comments.

18 Comments

Check for existence is simply indexing the map. Since if it's not in it, the zero value (which is false) will properly tell that. No need the comma-ok idiom for testing.
It is more optimal to use map[int]struct{} instead of bool, because an empty struct occupies 0 bytes in the memory. I've recently written a gist for this gist.github.com/bgadrian/cb8b9344d9c66571ef331a14eb7a2e80
That's not quite so easy. Just having to write that code everywhere you need to use a Set seems ridiculous to me these days. Collections support should be provided by any language. Think a better answer is that Go is not yet as mature. I'm sure there will be libraries to cover that soon enough.
Not easy nor intuitive. This is not a set, it is just a code pattern that behaves like a set. It is not a set since it doesn't store the data not provides operations as a set does. The correct answer is that GO doesn't have this functionality. Having a way to do something doesn't mean it is a reason not to have it.
so glad to reinvent the wheel
|
132

Partly, because Go doesn't have generics (so you would need one set-type for every type, or fall back on reflection, which is rather inefficient).

Partly, because if all you need is "add/remove individual elements to a set" and "relatively space-efficient", you can get a fair bit of that simply by using a map[yourtype]bool (and set the value to true for any element in the set) or, for more space efficiency, you can use an empty struct as the value and use _, present = the_setoid[key] to check for presence.

16 Comments

So if it doesn't have generics, how is the 'generic' map implemented at all?
@FerminSilva It's done by the compiler, using methods not directly accessible to developers.
Note that if you want to save bytes, you can use map[T]struct{} instead of map[T]bool.
The usual answer for golang question: "Why provide a feature when you can rewrite it in just a few lines?". This is why something that can be done in 3 explicit lines in python (or many other languages) takes 50+ obscure lines in go. This is one of the reasons (along with single letter variables) why I hate reading go code. It's uselessly long, just doing with for loops what should be done by a clear, efficient and well tested properly named function. Go "spirit" is just throwing away 50 years of good software engineering practice with dubious justifications.
|
5

Another possibility is to use bit sets, for which there is at least one package or you can use the built-in big package. In this case, basically you need to define a way to convert your object to an index.

1 Comment

I should note that I wrote the original version of the bitset package referenced above.
1

Like Vatine wrote: Since go lacks generics it would have to be part of the language and not the standard library. For that you would then have to pollute the language with keywords set, union, intersection, difference, subset...

The other reason is, that it's not clear at all what the "right" implementation of a set is:

  1. There is a functional approach:

    func IsInEvenNumbers(n int) bool {
        if n % 2 == 0 {
            return true
        }
       return false
    }
    

This is a set of all even ints. It has a very efficient lookup and union, intersect, difference and subset can easily be done by functional composition.

  1. Or you do a has-like approach like Dali showed.

A map does not have that problem, since you store something associated with the value.

5 Comments

To handle buit-in sets, Pascal overloads a bunch of binary (two-argmnent) operators: + for union, - for difference, * for intersection, <= for subset, >= for superset, = for equality, <> for inequality and in for membership. So in Go, it would be only a single new keyword -- in. On the other hand, Pascal's built-in sets only work on "ordinals"--that is, any type which has an underlying representation of an integer value of some size.
The fact that there are multiple ways to implement a set hasn't stopped many other languages from providing them.
@kostix: Go could even use the syntax s[key] (as if s were a map[T]bool) instead of key in s.
Any reason for not simply returning n % 2 == 0?
I know this is 5 years old, but pollute the language with keywords set, union, intersection, difference, subset, really? Beside set the rest are operations on sets, so they are functions.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.