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

问题描述 投票:0回答:15

假设我有一个学生城市列表,其大小可能是 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]
}

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

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

arrays go slice
15个回答
43
投票

我发现Burak'sFazlan's解决方案很有帮助。基于此,我实现了一些简单的函数,有助于使用通用方法从字符串、整数或任何其他类型的切片中删除或过滤重复数据。

这是我的三个函数,第一个是通用函数,第二个是字符串函数,最后一个是切片整数函数。您必须传递数据并返回所有唯一值作为结果。

通用解决方案: => Go v1.18

func removeDuplicate[T string | int](sliceList []T) []T {
    allKeys := make(map[T]bool)
    list := []T{}
    for _, item := range sliceList {
        if _, value := allKeys[item]; !value {
            allKeys[item] = true
            list = append(list, item)
        }
    }
    return list
}

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

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 链接:https://go.dev/play/p/iyb97KcftMa


9
投票

添加这个对我有用的答案,确实需要/包括排序。

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]
}

为了好玩,我尝试使用泛型! (仅限 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 测试链接:https://go.dev/play/p/bw1PP1osJJQ


6
投票

您可以在地图引导下进行就地更换:

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]

5
投票

减少内存使用:

package main

import (
    "fmt"
    "reflect"
)

type void struct{}

func main() {

    digits := [6]string{"one", "two", "three", "four", "five", "five"}

    set := make(map[string]void)

    for _, element := range digits {
        set[element] = void{}
    }

    fmt.Println(reflect.ValueOf(set).MapKeys())
}

附注游乐场


2
投票

简单易懂。

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
}

2
投票

尝试:https://github.com/samber/lo#uniq

names := lo.Uniq[string]([]string{"Samuel", "John", "Samuel"})
// []string{"Samuel", "John"}

1
投票

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

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

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

1
投票

排序然后压缩,例如像这样:

package main

import (
    "fmt"

    "golang.org/x/exp/slices"
)

func main() {
    list := []string{"c", "a", "b", "c", "b", "b"}
    slices.Sort[string](list)
    fmt.Println(slices.Compact[[]string, string](list)) // [a b c]
}

0
投票

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

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]

0
投票
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
}

将重复的拼接发送到上述函数,这将返回具有唯一元素的拼接。

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

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

0
投票

基于 Riyaz 的 解决方案,从 Go 1.18 开始就可以使用泛型了

func removeDuplicate[T string | int](tSlice []T) []T {
    allKeys := make(map[T]bool)
    list := []T{}
    for _, item := range tSlice {
        if _, value := allKeys[item]; !value {
            allKeys[item] = true
            list = append(list, item)
        }
    }
    return list
}

泛型最大限度地减少代码重复。

Go Playground 链接:https://go.dev/play/p/Y3fEtHJpP7Q


0
投票

到目前为止@snassr已经给出了最好的答案,因为它是在内存(没有额外内存)和运行时(nlogn)方面最优化的方式。但我想在这里强调的一件事是,如果我们想删除数组的任何索引/元素,我们应该从头到尾循环,因为它会降低复杂性。如果我们从头到尾循环,那么如果我们删除 nth

 索引,那么我们会意外地错过第 n 个元素(在删除第 n 个元素之前是 
n+1th
),因为在下一次迭代中我们将获得 
n+1th
 元素。

示例代码

func Dedup(strs []string) { sort.Strings(strs) for i := len(strs) - 1; i > 0; i-- { if strs[i] == strs[i-1] { strs = append(strs[:i], strs[i+1:]...) } } }
    

0
投票

Go 1.20 添加了 comparable

 泛型类型
,这使得可以扩展 Riyaz' 答案,如下所示:

func dedupeSlice[T comparable](sliceList []T) []T { dedupeMap := make(map[T]struct{}) list := []T{} for _, slice := range sliceList { if _, exists := dedupeMap[slice]; !exists { dedupeMap[slice] = struct{}{} list = append(list, slice) } } return list }
    

-1
投票
这是一个基于无地图索引的切片的重复“删除器”/修剪器。它使用排序方法。

n

值始终比非重复元素总数低 1 个值,这是因为此方法将当前(连续/单个)元素与下一个(连续/单个)元素进行比较,并且最后一个元素之后没有匹配项,因此您可以必须填充它以包括最后一个。

请注意,此代码片段不会将重复元素清空为 nil 值。然而,由于

n+1

 整数从重复项的索引开始,您可以从该整数开始循环,并将其余元素置零。

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)
    

-4
投票

在 go 中删除数组中的重复元素

func main() { arr := []int{1,3,2,3,3,5} var i,j int var size = len(arr) for i=0;i<size;i++ { for j=i+1;j<size;j++ { if arr[i]==arr[j] { for k:=j;k<size-1;k++ { arr[k]=arr[k+1] } size-- j-- } } } for z:=0;z<size;z++ { fmt.Printf("%d\n",arr[z]) } }
    
© www.soinside.com 2019 - 2024. All rights reserved.