arrays - 在 Go slice 或数组中查找唯一项

标签 arrays go set unique slice

我是新手,现在我真的、真的很困惑。

假设我有一个坐标列表,假设我在这个坐标列表中有一些 double 。我这辈子都想不出如何制作一份独特的 list 。通常在 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

最佳答案

您的代码中存在多个错误。最严重的是,由于您将 visited slice 的每个特定元素与 unique所有元素进行比较,您最终会如果 unique 至少包含一个不同的,则附加它。继续前进,如果 unique 中有更多元素,您最终将多次附加它,因为您的内部 for 循环不会“中断”。这不是您想要的,您想要附加等于 uniquenone 的元素。

另请注意,如果每个字段都是可比较的,则 Go 中的 struct 也是可比较的。由于您的 visit 结构仅包含 2 个 int 字段,因此它具有可比性,因此您可以简单地使用 = 比较 visit 类型的值= 运算符,不需要那个丑陋的 reflect.DeepEqual()。参见 Spec: Comparison operators :

Struct values are comparable if all their fields are comparable. Two struct values are equal if their corresponding non-blank fields are equal.

这是应用您的逻辑的简化、正确的版本:

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 Playground 上尝试):

[{1 100} {2 2} {1 1}]

备选

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

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)

输出(在 Go Playground 上尝试):

map[{2 2}:true {1 1}:true {1 100}:true]

唯一的“列表”是映射中键的列表。

如果您希望将唯一的 visit 值作为一个 slice ,请参阅以下变体:

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

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

fmt.Println(unique)

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

[{1 100} {2 2} {1 1}]

请注意,如果 v 已经在映射中(作为键,true 是我们存储在 map 中的值)。如果 v 尚未在映射中,则 m[v] 生成值类型的零值,对于 类型为 false >bool,正确地表明值 v 还不在 map 中。参见 Spec: Index expressions :

For a of map type M:

...if the map is nil or does not contain such an entry, a[x] is the zero value for the value type of M

关于arrays - 在 Go slice 或数组中查找唯一项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34111476/

相关文章:

javascript - 将一个数组映射到具有负索引偏移量的第二个数组

javascript - 将长的 JavaScript 数组传递给代码隐藏?

c++ - 如何从 C++ DLL 回调 GO 方法

C++ 使用成员字段设置比较器

arrays - 在 VBA 中,如何将字符串拆分为数组,然后将其作为参数传递给 Sub 或 Function

javascript - 在关联数组中添加数据

json - 如何在解码 JSON 时检查提供的 api 字段的类型

go - 在 Kubernetes 中,Go 容器是否会在另一个容器使用内核时使用所有内核

set - f : R->R or f:Z->R mean in discrete math? 到底是什么

c++ - 如何将集合中的每个标记与 C++ 中的一组标记相关联