1

I have a map of slices that I need to remove duplicates from. I think I'm close to the solution but I'm missing something that I can't quite figure out.

Expected output: map[key1:[1 2 3] key2:[1 2 3]]

Actual output: map[key2:[1 2 3]]

    package main

    import "fmt"

    func main() {
        x := make(map[string][]string)
        keys := make(map[string]bool)
        result := make(map[string][]string)

        x["key1"] = append(x["key1"], "1")
        x["key1"] = append(x["key1"], "1")
        x["key1"] = append(x["key1"], "2")
        x["key1"] = append(x["key1"], "3")
        x["key1"] = append(x["key1"], "3")
        x["key2"] = append(x["key2"], "1")
        x["key2"] = append(x["key2"], "2")
        x["key2"] = append(x["key2"], "2")
        x["key2"] = append(x["key2"], "3")

        fmt.Println(x)

        for k, e := range x{
            for _, v := range e {
                if _, val := keys[v]; !val {
                    keys[v] = true
                    result[k] = append(result[k], v)
                }
            }
        }
        fmt.Println(result)
    }

Example on the playground here: Go Playground

poldy
  • 204
  • 3
  • 12

1 Answers1

1

You want all slices to be handled separately. E.g. you want to remove duplicates from the slice denoted by x["key1"], and you want to remove duplicates from the slice denoted by x["key2"]. Which means you should "reset" keys when a new slice is being processed, yet you only initialize it once.

As a consequence, if (by any chance) you process x["key1"] first, and then you proceed to x["key2"], since x["key1"] contains all the elements that x["key2"] does, no new elements will be found, so "key2" will be left out from the result entirely.

Simply initialize keys before each slice, inside the loop:

for k, e := range x {
    keys := make(map[string]bool)
    for _, v := range e {
        if _, val := keys[v]; !val {
            keys[v] = true
            result[k] = append(result[k], v)
        }
    }
}

With this, the output will be (try it on the Go Playground):

map[key1:[1 1 2 3 3] key2:[1 2 2 3]]
map[key1:[1 2 3] key2:[1 2 3]]

Also note that you can use a composite literal to create your initial map in a compact way like this:

x := map[string][]string{
    "key1": {"1", "1", "2", "3", "3"},
    "key2": {"1", "2", "2", "3"},
}

And since you use bool in your keys map, you may simply check for existing elements like this (because indexing a map with a key that is not in it results in the zero value of the map's value type, which for bool is false, properly telling the element is not in the map):

if !keys[v] {
    keys[v] = true
    result[k] = append(result[k], v)
}

Try this one on the Go Playground.

You can read more about this here: How to check if a map contains a key in Go?; and How can I create an array that contains unique strings?

icza
  • 389,944
  • 63
  • 907
  • 827
  • Yes, that makes perfect sense. Thank you for pointing that out. I was staring at it too long to see it :) – poldy Oct 29 '19 at 19:15