如何从 Go 中的 Slice 中删除重复的字符串或 int

How to remove duplicates strings or int from Slice in Go

假设我有一个学生城市列表,它的大小可能是 100 或 1000,我想过滤掉所有重复的 个城市。

我想要一个通用的解决方案,可以用来从任何切片中删除所有重复的字符串。

我是 Go 语言的新手,所以我尝试通过循环并使用另一个循环函数检查元素是否存在来做到这一点。

学生城市列表(数据):

studentsCities := []string{"Mumbai", "Delhi", "Ahmedabad", "Mumbai", "Bangalore", "Delhi", "Kolkata", "Pune"}

我创建的函数,它正在完成工作:

func contains(s []string, e string) bool {
    for _, a := range s {
        if a == e {
            return true
        }
    }
    return false
}

func removeDuplicates(strList []string) []string {
    list := []string{}
    for _, item := range strList {
        fmt.Println(item)
        if contains(list, item) == false {
            list = append(list, item)
        }
    }
    return list
}

我的解决方案测试

func main() {
    studentsCities := []string{"Mumbai", "Delhi", "Ahmedabad", "Mumbai", "Bangalore", "Delhi", "Kolkata", "Pune"}

    uniqueStudentsCities := removeDuplicates(studentsCities)
    
    fmt.Println(uniqueStudentsCities) // Expected output [Mumbai Delhi Ahmedabad Bangalore Kolkata Pune]
}

我认为我尝试的上述解决方案不是最佳解决方案。因此,我需要你们的帮助建议从切片中删除重复项的最快方法?

我查了Whosebug,还没有人问这个问题,所以我没有得到任何解决方案。

您可以在地图的指导下进行就地替换:

processed := map[string]struct{}{}
w := 0
for _, s := range cities {
    if _, exists := processed[s]; !exists {
        // If this city has not been seen yet, add it to the list
        processed[s] = struct{}{}
        cities[w] = s
        w++
    }
}
cities = cities[:w]

如果不想浪费内存分配另一个数组来复制值,可以原地删除值,如下所示:

package main

import "fmt"

var studentsCities = []string{"Mumbai", "Delhi", "Ahmedabad", "Mumbai", "Bangalore", "Delhi", "Kolkata", "Pune"}

func contains(s []string, e string) bool {
    for _, a := range s {
        if a == e {
            return true
        }
    }
    return false
}

func main() {
    fmt.Printf("Cities before remove: %+v\n", studentsCities)
    for i := 0; i < len(studentsCities); i++ {
        if contains(studentsCities[i+1:], studentsCities[i]) {
            studentsCities = remove(studentsCities, i)
            i--
        }
    }
    fmt.Printf("Cities after remove: %+v\n", studentsCities)
}
func remove(slice []string, s int) []string {
    return append(slice[:s], slice[s+1:]...)
}

结果:

Cities before remove: [Mumbai Delhi Ahmedabad Mumbai Bangalore Delhi Kolkata Pune]
Cities after remove: [Ahmedabad Mumbai Bangalore Delhi Kolkata Pune]
func UniqueNonEmptyElementsOf(s []string) []string {
    unique := make(map[string]bool, len(s))
    var us []string
    for _, elem := range s {
        if len(elem) != 0 {
            if !unique[elem] {
                us = append(us, elem)
                unique[elem] = true
            }
        }
    }

    return us
}

将复制的拼接发送到上述函数,这将return具有独特元素的拼接。

func main() {
    studentsCities := []string{"Mumbai", "Delhi", "Ahmedabad", "Mumbai", "Bangalore", "Delhi", "Kolkata", "Pune"}

    uniqueStudentsCities := UniqueNonEmptyElementsOf(studentsCities)
    
    fmt.Println(uniqueStudentsCities)
}

我发现 Burak's and Fazlan's 解决方案很有帮助。基于此,我实现了有助于 remove/filter 从字符串、整数等切片中复制数据的简单函数

这是我的两个函数,一个用于字符串,另一个用于切片的整数。您必须传递数据和 return 所有唯一值作为结果。

要从切片中删除重复的字符串:

func removeDuplicateStr(strSlice []string) []string {
    allKeys := make(map[string]bool)
    list := []string{}
    for _, item := range strSlice {
        if _, value := allKeys[item]; !value {
            allKeys[item] = true
            list = append(list, item)
        }
    }
    return list
}

要从切片中删除重复的整数:

func removeDuplicateInt(intSlice []int) []int {
    allKeys := make(map[int]bool)
    list := []int{}
    for _, item := range intSlice {
        if _, value := allKeys[item]; !value {
            allKeys[item] = true
            list = append(list, item)
        }
    }
    return list
}

您可以更新切片类型,它会过滤掉所有类型切片的所有重复数据。

这里是 GoPlayground link:https://play.golang.org/p/IyVWlWRQM89

也可以用类似集合的地图来完成:

ddpStrings := []string{}
m := map[string]struct{}{}

for _, s := range strings {
    if _, ok := m[scopeStr]; ok {
        continue
    }
    ddpStrings = append(ddpStrings, s)
    m[s] = struct{}{}
}

这是基于无地图索引的切片的重复“去除器”/修剪器。它使用排序方法。

n 值始终比非重复元素的总数低 1,这是因为此方法将当前 (consecutive/single) 元素与下一个 (consecutive/single) 元素进行比较在最后一个之后没有匹配,所以你必须填充它以包括最后一个。

请注意,此代码段不会将重复元素清空为 nil 值。但是,由于 n+1 整数从重复项目的索引开始,您可以从所述整数开始循环并 nil 其余元素。

sort.Strings(strs)
for n, i := 0, 0; ; {
    if strs[n] != strs[i] {
        if i-n > 1 {
            strs[n+1] = strs[i]
        }
        n++
    }
    i++
    if i == len(strs) {
        if n != i {
            strs = strs[:n+1]
        }
        break
    }
}
fmt.Println(strs)

简单易懂。

func RemoveDuplicate(array []string) []string {
    m := make(map[string]string)
    for _, x := range array {
        m[x] = x
    }
    var ClearedArr []string
    for x, _ := range m {
        ClearedArr = append(ClearedArr, x)
    }
    return ClearedArr
}

添加这个对我有用的答案,但是 require/include 排序。

func removeDuplicateStrings(s []string) []string {
    if len(s) < 1 {
        return s
    }

    sort.Strings(s)
    prev := 1
    for curr := 1; curr < len(s); curr++ {
        if s[curr-1] != s[curr] {
            s[prev] = s[curr]
            prev++
        }
    }

    return s[:prev]
}

为了好玩,我尝试使用泛型! (仅限 Go 1.18+)

type SliceType interface {
    ~string | ~int | ~float64 // add more *comparable* types as needed
}

func removeDuplicates[T SliceType](s []T) []T {
    if len(s) < 1 {
        return s
    }

    // sort
    sort.SliceStable(s, func(i, j int) bool {
        return s[i] < s[j]
    })

    prev := 1
    for curr := 1; curr < len(s); curr++ {
        if s[curr-1] != s[curr] {
            s[prev] = s[curr]
            prev++
        }
    }

    return s[:prev]
}

Go Playground Link 进行测试:https://go.dev/play/p/bw1PP1osJJQ