英文:
Finding Unique Items in a Go Slice or Array
问题
我对Go语言还不太熟悉,现在感到非常困惑。
假设我有一个坐标列表,列表中可能包含一些重复的坐标。我无法弄清楚如何创建一个唯一的列表。通常在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)
// 遍历visited列表,找出唯一的元素
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)
}
你可以在Playground上运行这段代码。
希望这能帮到你!
英文:
I'm pretty new to go and I'm really, really confused right now.
Let's say I have a list of coordinates and lets say I have some doubles in this list of coordinates. I can't for the life of me figure out how to make a unique list. Normally in Python I can "cheat" with sets and other built-ins. Not so much in 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)
}
<kbd>Run it on Playground</kbd>
答案1
得分: 17
你的代码中有多个错误。最严重的错误是,由于你将visited
切片的每个特定元素与unique
的所有元素进行比较,所以如果unique
至少包含一个不同的元素,你将会将它追加到unique
中。而且,如果unique
中有更多不同的元素,你的内部for
循环不会“中断”,所以你最终会多次将它追加到unique
中。这不是你想要的,你想要追加与unique
中没有一个相等的元素。
此外,请注意,在Go中,如果结构体的每个字段都是可比较的,那么该结构体是可比较的。由于你的visit
结构体只包含两个int
字段,它是可比较的,因此你可以使用==
运算符直接比较visit
类型的值,而不需要使用丑陋的reflect.DeepEqual()
。参见规范:比较运算符:
> 如果结构体的所有字段都是可比较的,则结构体值是可比较的。如果它们对应的非空白字段相等,则两个结构体值相等。
下面是一个简化且正确的版本,应用了你的逻辑:
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
可以作为映射中的键,因为它是可比较的(参见上文)。
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
值作为切片输出,可以使用以下变体:
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}]
请注意,索引表达式m[v]
的结果为true
,如果v
已经在映射中(作为键,true
是我们在映射中存储的值)。如果v
尚未在映射中,m[v]
将产生值类型的零值,对于bool
类型来说,这个零值是false
,正确地表示值v
尚未在映射中。参见规范:索引表达式:
> 对于映射类型M
的a
:
>
> ……如果映射是nil
或不包含这样的条目,则a[x]
是M
值的零值
英文:
There are multiple errors in your code. The most serious is that since you're comparing each specific element of the visited
slice to all of the elements of unique
, you will end up appending it if unique
contains at least one which is different. And going forward, you will end up appending it multiple times if there are more elements in unique
which differ as your inner for
loop doesn't "break". This is not what you want, you want to append elements which equals to none of unique
.
Also note that a struct
in Go is comparable if each of its fields are comparable. Since your visit
struct contains only 2 int
fields, it is comparable and so you can compare values of visit
type simply with the ==
operator, no need that ugly reflect.DeepEqual()
. See 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.
Here's a simplified, correct version that applies your logic:
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)
Output (try it on the Go Playground):
[{1 100} {2 2} {1 1}]
Alternative
It's true that Go doesn't have a built-in set type, but you can use a map[visit]bool
easily as a set. With that, it becomes really simple! Note that visit
can be used as key in the map because it is comparable (see above).
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)
Output (try it on the Go Playground):
map[{2 2}:true {1 1}:true {1 100}:true]
The unique "list" is the list of keys in the map.
If you want the unique visit
values as a slice, see this variant:
var unique []visit
m := map[visit]bool{}
for _, v := range visited {
if !m[v] {
m[v] = true
unique = append(unique, v)
}
}
fmt.Println(unique)
Output (as expected, try it on the Go Playground):
[{1 100} {2 2} {1 1}]
Note that this index expression: m[v]
evaluates to true
if v
is already in the map (as a key, true
is the value we stored in the map). If v
is not yet in the map, m[v]
yields the zero value of the value type which is false
for the type bool
, properly telling that the value v
is not yet in the map. See 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
答案2
得分: 3
我认为你可以使用Map来解决这个谜题。
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)
}
我已经将代码翻译成中文,你可以查看上面的代码。
英文:
I think you can use help of Map to solve this puzzle
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)
}
答案3
得分: 2
我认为你可以创建一个访问记录的地图。就像这样:
visited := make(map[visit]bool)
然后你可以设置访问记录的值:
visited[visit] = true
最后,你可以通过以下代码获取所有已访问的位置:
var unique []visit
for k := range visited {
unique = append(unique, k)
}
你可以参考这篇文章了解更多关于Go语言中地图的使用:Go Maps in Action
英文:
i think you can make a map of visits.
Something like this
visited := make(map[visit]Boolean)
than you can set the value of the visited.
visited[visit]=true
and lastly, you can fetch all visited locations by this code
for k, _ := range visited {
unique = append(unique, k)
}
答案4
得分: 0
你可以通过sort.Slice()
对切片进行排序,然后通过用后续的唯一元素覆盖非唯一元素来使切片变得唯一:
func Unique[T comparable](xs []T) []T {
n := len(xs)
if n == 0 {
return xs
}
j := 0
for i := 1; i < n; i++ {
if xs[j] != xs[i] {
j++
if j < i {
xs[j] = xs[i]
for k := i + 1; k < n; k++ {
if xs[j] != xs[k] {
j++
xs[j] = xs[k]
}
}
break
}
}
}
xs = xs[0:j+1]
return xs
}
使切片变得唯一只需要线性的工作量,然而,这在排序时被主导(除非你的输入已经排序),排序的时间复杂度为O(n log n)
。
示例用法:
visited = sort.Slice(visited, func(i, j int) bool {
return visited[i].x < visited[j].x && visited[i].y < visited[j].y })
visited = Unique(visited)
unique := visited
另外,如果你不想排序或者不关心元素的顺序,你可以将所有元素存储在类似集合的映射中,这样可以消除重复。
示例:
h := map[visit]struct{} {}
for _, v := range visited {
h[v] = struct{} {}
}
unique := make(visit[], len(h))
i := 0
for k := range h {
unique[i] = k
i++
}
总运行时间是线性的,除非你的数据哈希得很差。
英文:
You can sort your slice via sort.Slice()
and then make the slice unique by overwriting non-unique elements with following unique ones:
func Unique[T comparable](xs []T) []T {
n := len(xs)
if n == 0 {
return xs
}
j := 0
for i := 1; i < n; i++ {
if xs[j] != xs[i] {
j++
if j < i {
xs[j] = xs[i]
for k := i + 1; k < n; k++ {
if xs[j] != xs[k] {
j++
xs[j] = xs[k]
}
}
break
}
}
}
xs = xs[0:j+1]
return xs
}
Making the slice unique is only linear effort, however, this is dominated by the sorting (unless your input already is sorted), which is in O(n log n)
.
Example usage:
visited = sort.Slice(visited, func(i, j int) bool {
return visited[i].x < visited[j].x && visited[i].y < visited[j].y })
visited = Unique(visited)
unique := visited
Alternatively, if you don't want to sort or don't care about the element order you can store all elements in a set-like map, which eliminates duplicates.
Example:
h := map[visit]struct{} {}
for _, v := range visited {
h[v] = struct{} {}
}
unique := make(visit[], len(h))
i := 0
for k := range h {
unique[i] = k
i++
}
The total runtime is linear, unless your data hashes badly.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论