在Go Slice或Array中查找唯一项

时间:2015-12-05 22:06:59

标签: arrays go set unique slice

我很新,我现在真的很困惑。

假设我有一个坐标列表,让我说这个坐标列表中有一些双打。我不能为我的生活弄清楚如何制作一个独特的清单。通常在Python中,我可以使用集合和其他内置函数“作弊”。在Go中没那么多。

package main

import (
    "fmt"
    "reflect"
)

type visit struct {
    x, y int
}

func main() {
    var visited []visit
    var unique []visit

    visited = append(visited, visit{1, 100})
    visited = append(visited, visit{2, 2})
    visited = append(visited, visit{1, 100})
    visited = append(visited, visit{1, 1})

    unique = append(unique, visit{1, 1})

    fmt.Println(unique)

    // Go through the visits and find the unique elements
    for _, v := range visited {
        for _, u := range unique {

            fmt.Printf("Here's unique: %v\n", unique)
            fmt.Printf("Comparing %v to %v is %v\n", v, u, reflect.DeepEqual(v, u))

            if reflect.DeepEqual(v, u) {
                fmt.Println("Skip")
            } else {
                unique = append(unique, v)
            }
        }
    }

    fmt.Println(unique)
}

Run it on Playground

3 个答案:

答案 0 :(得分:8)

您的代码中存在多个错误。最严重的是,因为您要将visited切片的每个特定元素与unique元素的所有进行比较,所以如果{{1},您最终会附加它包含至少一个不同的。如果unique中有更多元素因内部unique循环没有“中断”而有所不同,那么最后会多次追加它。这不是你想要的,你想要追加等于for的元素。

另请注意,如果每个字段都具有可比性,则Go中的unique具有可比性。由于您的struct结构只包含2个visit字段,因此它具有可比性,因此您可以简单地将int类型的值与visit运算符进行比较,而不需要那么丑= 1}}。见Spec: Comparison operators

  

如果所有字段都具有可比性,则结构值具有可比性。如果相应的非blank字段相等,则两个struct值相等。

这是一个适用于您的逻辑的简化版本:

reflect.DeepEqual()

输出(在Go Playground上尝试):

visited := []visit{
    visit{1, 100},
    visit{2, 2},
    visit{1, 100},
    visit{1, 1},
}
var unique []visit

for _, v := range visited {
    skip := false
    for _, u := range unique {
        if v == u {
            skip = true
            break
        }
    }
    if !skip {
        unique = append(unique, v)
    }
}

fmt.Println(unique)

替代

Go确实没有内置的集合类型,但您可以轻松地使用[{1 100} {2 2} {1 1}] 作为集合。有了它,它变得非常简单!请注意,map[visit]bool可以用作地图中的关键字,因为它具有可比性(见上文)。

visit

输出(在Go Playground上尝试):

visited := []visit{
    visit{1, 100},
    visit{2, 2},
    visit{1, 100},
    visit{1, 1},
}
unique := map[visit]bool{}

for _, v := range visited {
    unique[v] = true
}

fmt.Println(unique)

唯一的“列表”是地图中的键列表。

如果您希望将唯一map[{2 2}:true {1 1}:true {1 100}:true] 值作为切片,请参阅以下变体:

visit

输出(按预期,在Go Playground上尝试):

var unique []visit
m := map[visit]bool{}

for _, v := range visited {
    if !m[v] {
        m[v] = true
        unique = append(unique, v)
    }
}

fmt.Println(unique)

请注意,如果[{1 100} {2 2} {1 1}] 已经在地图中,则此索引表达式m[v]的结果为true(作为关键字,v是我们存储在地图中的值)。如果地图中尚未显示true,则v会为类型m[v]生成false值类型的零值,并正确告知值{{1}尚未出现在地图中。见Spec: Index expressions

  

适用于map type bool

     

...如果地图为v或不包含此类条目,M为值nil的{​​{3}}

答案 1 :(得分:2)

我认为您可以进行map次访问。 像这样的东西

visited := make(map[visit]Boolean)

比您可以设置被访问的值。

visited[visit]=true

最后,您可以通过此代码

获取所有访问过的位置
for k, _ := range visited {
    unique = append(unique, k)
}

答案 2 :(得分:1)

我认为您可以使用Map的帮助来解决这个难题

https://play.golang.org/p/b7JtHYQ3N8

package main

import (
    "fmt"
)

type visit struct {
    x, y int
}

func main() {
    var visited []visit
    var unique []visit

    uniqueMap := map[visit]int{}

    visited = append(visited, visit{1, 100})
    visited = append(visited, visit{2, 2})
    visited = append(visited, visit{1, 100})
    visited = append(visited, visit{1, 1})

    for _, v := range visited {
        if _, exist := uniqueMap[v]; !exist {
            uniqueMap[v] = 1
            unique = append(unique, v)
        } else {
            uniqueMap[v]++
        }
    }
    fmt.Printf("Uniques: %v\nMaps:%v\n", unique, uniqueMap)
}
相关问题