118

I'm looking for something like the c++ function .clear() for the primitive type map.

Or should I just create a new map instead?

Update: Thank you for your answers. By looking at the answers I just realized that sometimes creating a new map may lead to some inconsistency that we don't want. Consider the following example:

var a map[string]string
var b map[string]string

func main() {
    a = make(map[string]string)
    b=a
    a["hello"]="world"
    a = nil
    fmt.Println(b["hello"])
}

I mean, this is still different from the .clear() function in c++, which will clear the content in the object.

lavin
  • 2,276
  • 2
  • 13
  • 15
  • 1
    also see this discussion: https://groups.google.com/d/topic/golang-nuts/6yHDC7IYCj4/discussion – perreal Dec 11 '12 at 02:08

7 Answers7

157

You should probably just create a new map. There's no real reason to bother trying to clear an existing one, unless the same map is being referred to by multiple pieces of code and one piece explicitly needs to clear out the values such that this change is visible to the other pieces of code.

So yeah, you should probably just say

mymap = make(map[keytype]valtype)

If you do really need to clear the existing map for whatever reason, this is simple enough:

for k := range m {
    delete(m, k)
}
Lily Ballard
  • 182,031
  • 33
  • 381
  • 347
  • On the one hand, it means rolling your own stuff more often, but on the other hand, it's a heck of a lot simpler. – Lily Ballard Dec 11 '12 at 02:08
  • Thank you sir! :) I think I'll like this style as I write more code in Go. – lavin Dec 11 '12 at 02:27
  • 11
    Is it really OK to modify the contents of a map while iterating through all values? Other languages will not work correctly with this. – John Jeffery Dec 19 '12 at 11:57
  • 7
    @JohnJeffery: I tested this before I posted it. Seems to work. The actual language from the spec says `The iteration order over maps is not specified and is not guaranteed to be the same from one iteration to the next. If map entries that have not yet been reached are deleted during iteration, the corresponding iteration values will not be produced. If map entries are inserted during iteration, the behavior is implementation-dependent, but the iteration values for each entry will be produced at most once. If the map is nil, the number of iterations is 0.` This suggests it's supported. – Lily Ballard Dec 19 '12 at 21:13
  • @KevinBallard, I will not suggest deleting item during iterating itself just like undefined behavior in other language, although it seems find in Golang. – andy Mar 09 '16 at 12:20
  • @andy: The language specification suggests that it's perfectly fine to delete entries from a map during iteration with `range`. Specifically, it says that if map entries not yet reached are removed during iteration they will be skipped, and if map entries are created they may or may not be produced by iteration. It doesn't document what happens if map entries already reached are deleted, but the previous rules strongly imply that it's perfectly fine to delete already-produced entries from a map during iteration. – Lily Ballard Mar 10 '16 at 19:47
  • 7
    Updated release notes link for the compiler optimization mentioned above: https://golang.org/doc/go1.11#performance-compiler – Nathan Baulch Jun 10 '21 at 01:24
32

Unlike C++, Go is a garbage collected language. You need to think things a bit differently.

When you make a new map

a := map[string]string{"hello": "world"}
a = make(map[string]string)

the original map will be garbage-collected eventually; you don't need to clear it manually. But remember that maps (and slices) are reference types; you create them with make(). The underlying map will be garbage-collected only when there are no references to it. Thus, when you do

a := map[string]string{"hello": "world"}
b := a
a = make(map[string]string)

the original array will not be garbage collected (until b is garbage-collected or b refers to something else).

John Smith
  • 980
  • 1
  • 8
  • 15
  • 10
    `Unlike C++, Go is a garbage collected language. You need to think things a bit differently.` Java, Python, C# have `clear` and they're all garbage collected :D – Spidey Apr 18 '22 at 13:35
12

Go 1.21

Use the builtin clear.

func main() {
    m := map[string]int{"foo": 1}
    clear(m)
}

It's a single function call, and has the minor but important advantage that it will delete irreflexive keys (see below for details about what this means). The standard library maps package doesn't have maps.Clear anymore.

Go 1.18 to 1.20

You can use maps.Clear. The function belongs to the package golang.org/x/exp/maps (experimental and not covered by the compatibility guarantee)

Clear removes all entries from m, leaving it empty.

Example usage:

func main() {
    testMap := map[string]int{"gopher": 1, "badger": 2}
    maps.Clear(testMap)
    fmt.Println(testMap)
    
    testMap["zebra"] = 2000
    fmt.Println(testMap)
}

Playground: https://go.dev/play/p/qIdnGrd0CYs?v=gotip

If you don't want to depend on experimental packages, you can copy-paste the source, which is actually extremely simple:

func Clear[M ~map[K]V, K comparable, V any](m M) {
    for k := range m {
        delete(m, k)
    }
}

IMPORTANT NOTE: just as with the builtin delete — which the implementation of maps.Clear uses —, this does not remove irreflexive keys from the map. The reason is that for irreflexive keys, by definition, x == x is false. Irreflexive keys are NaN floats and every other type that supports comparison operators but contains NaN floats somewhere.

See this code to understand what this entails:

func main() {
    m := map[float64]string{}
    m[1.0] = "foo"

    k := math.NaN()
    fmt.Println(k == k) // false
    m[k] = "bar"

    maps.Clear(m)
    fmt.Printf("len: %d, content: %v\n", len(m), m) 
    // len: 1, content: map[NaN:bar]

    a := map[[2]float64]string{}
    a[[2]float64{1.0, 2.0}] = "foo"

    h := [2]float64{1.0, math.NaN()}
    fmt.Println(h == h) // false
    a[h] = "bar"

    maps.Clear(a)
    fmt.Printf("len: %d, content: %v\n", len(a), a) 
    // len: 1, content: map[[1 NaN]:bar]
}

Playground: https://go.dev/play/p/LWfiD3iPA8Q

blackgreen
  • 34,072
  • 23
  • 111
  • 129
10
// Method - I , say book is name of map
for k := range book {
    delete(book, k)
}

// Method - II
book = make(map[string]int)

// Method - III
book = map[string]int{}
Sumer
  • 2,687
  • 24
  • 24
  • 2
    Methods II and III do not clear the map. If there is a copy of book somewhere then the behaviour is different. Eg see https://go.dev/play/p/H0VqSTkTC6m – AJR Jun 08 '23 at 00:41
1

For the method of clearing a map in Go

for k := range m {
    delete(m, k)
}

It only works if m contains no key values containing NaN.

delete(m, k) doesn't work for any irreflexive key (such as math.NaN()), but also structs or other comparable types with any NaN float in it. Given struct{ val float64 } with NaN val is also irreflexive (Quote by blackgreen comment)


To resolve this issue and support clearing a map in Go, one buildin clear(x) function could be available in the new release, for more details, please refer to add clear(x) builtin, to clear map, zero content of slice, ptr-to-array

Per Go 1.21 release note

The new function clear deletes all elements from a map or zeroes all elements of a slice.

Call Argument type Result
clear(m) map[K]T deletes all entries, resulting in an empty map (len(m) == 0)
clear(s) []T sets all elements up to the length of s to the zero value of T
clear(t) type parameter see below

If the type of the argument to clear is a type parameter, all types in its type set must be maps or slices, and clear performs the operation corresponding to the actual type argument.

If the map or slice is nil, clear is a no-op.

zangw
  • 43,869
  • 19
  • 177
  • 214
  • 1
    this answer could be more accurate, `delete(m, k)` doesn't work for **any** irreflexive key, this includes NaN floats but also structs or other comparable types with any NaN float in it, e.g. `struct{ val float64 }` with NaN `val` is also irreflexive or `[2]float64{1, math.NaN()}` – blackgreen Nov 04 '22 at 08:37
0

Go 1.21 Release adds three new built-ins to the language. In particular:

  • The new function clear deletes all elements from a map or zeroes all elements of a slice.

It works like that:

m := map[string]string{"hello": "world"}
clear(m)
fmt.Println(m) // map[]

https://go.dev/play/p/UdQBUEeQ4ck?v=gotip

astef
  • 8,575
  • 4
  • 56
  • 95
-8

If you are trying to do this in a loop, you can take advantage of the initialization to clear out the map for you. For example:

for i:=0; i<2; i++ {
    animalNames := make(map[string]string)
    switch i {
        case 0:
            animalNames["cat"] = "Patches"
        case 1:
            animalNames["dog"] = "Spot";
    }

    fmt.Println("For map instance", i)
    for key, value := range animalNames {
        fmt.Println(key, value)
    }
    fmt.Println("-----------\n")
}

When you execute this, it clears out the previous map and starts with an empty map. This is verified by the output:

$ go run maptests.go 
For map instance 0
cat Patches
-----------

For map instance 1
dog Spot
-----------
leemic
  • 1
  • 4
    That's not clearing out the map but making a new map and binding to a local variable with the same name each loop. – Delaney Mar 28 '17 at 22:35