簡體   English   中英

如何從 Go 中的 Slice 中刪除重復的字符串或 int

[英]How to remove duplicates strings or int from Slice in Go

假設我有一個學生城市列表,它的大小可以是 100 或 1000,我想過濾掉所有重復的城市。

我想要一個通用的解決方案,可以用來從任何切片中刪除所有重復的字符串。

我是 Go 語言的新手,所以我嘗試通過循環並使用另一個循環 function 檢查元素是否存在來做到這一點。

學生城市列表(數據):

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,這個問題還沒有被問到,所以我沒有得到任何解決方案。

我發現BurakFazlan 的解決方案很有幫助。 基於此,我實現了一些簡單的函數,這些函數有助於使用通用方法從字符串、整數或任何其他類型的切片中刪除或過濾重復數據。

這是我的三個函數,第一個是通用的,第二個用於字符串,最后一個用於切片整數。 您必須傳遞您的數據並返回所有唯一值作為結果。

通用解決方案: => 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

您可以在 map 的指導下進行就地更換:

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]

但是,添加這個對我有用的答案確實需要/包括排序。

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

為了好玩,我嘗試使用 generics。 (僅限 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 游樂場鏈接與測試: https://go.dev/play/p/bw1PP1osJJQ

簡單易懂。

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
}

減少 memory 的使用:

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())
}

ps游樂場

如果您不想浪費 memory 分配另一個數組來復制值,您可以將值刪除,如下所示:

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
}

將復制的接頭發送到上述 function,這將返回具有唯一元素的接頭。

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

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

也可以使用類似集合的 map 來完成:

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,這是因為此方法將當前(連續/單個)元素與下一個(連續/單個)元素進行比較,並且在最后一個元素之后沒有匹配項,因此您必須填充它包括最后一個。

請注意,此代碼段不會將重復元素清空為 nil 值。 但是,由於n+1 integer 從重復項的索引開始,您可以從所述 integer 循環,並且將元素的 rest 歸零。

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)

基於Riyaz 的解決方案,您可以使用 generics 自 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
}

Generics 最大限度地減少了代碼重復。

Go 游樂場鏈接: https://go.dev/play/p/Y3fEtHJpP7Q

到目前為止,@snassr 給出了最佳答案,因為它是 memory(無額外內存)和運行時(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:]...)
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM