68

Go如何对数组切片进行去重

 4 years ago
source link: https://www.tuicool.com/articles/VF3yiy2
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Go标准库本身没有提供一个去除slice中重复元素的函数,需要自己去实现。下面提供一种实现思路,如果有更好的实现方法欢迎留言讨论。

package main

import (
    "fmt"
)

func main() {
    s := []string{"hello", "world", "hello", "golang", "hello", "ruby", "php", "java"}

    fmt.Println(removeDuplicateElement(s)) //output: hello world golang ruby php java
}

func removeDuplicateElement(languages []string) []string {
    result := make([]string, 0, len(languages))
    temp := map[string]struct{}{}
    for _, item := range languages {
        if _, ok := temp[item]; !ok {
            temp[item] = struct{}{}
            result = append(result, item)
        }
    }
    return result
}

解释

  • removeDuplicateElement 函数总共初始化两个变量,一个长度为0的 slice ,一个空 map 。由于 slice 传参是按引用传递,没有创建占用额外的内存空间。
  • map[string]struct{}{} 创建了一个key类型为String值类型为空 structmap ,等效于使用 make(map[string]struct{})
  • struct 不占内存空间,使用它来实现我们的函数空间复杂度是最低的。

适配多个切片类型

上面的去除重复元素的函数,只能处理字符串切片对于其他类型的切片就不行了。如果不想针对每种类型的切片都写一个去重函数的话可以使用Go的type-switch自己写一个可以处理多个切片类型的函数。下面是我写的一个实现:

package common

import (
    "fmt"
)


type sliceError struct {
    msg string
}

func (e *sliceError) Error() string {
    return e.msg
}

func Errorf(format string, args ...interface{}) error {
    msg := fmt.Sprintf(format, args...)
    return &sliceError{msg}
}

func removeDuplicateElement1(originals interface{}) (interface{}, error) {
    temp := map[string]struct{}{}
    switch slice := originals.(type) {
    case []string:
        result := make([]string, 0, len(originals.([]string)))
        for _, item := range slice {
            key := fmt.Sprint(item)
            if _, ok := temp[key]; !ok {
                temp[key] = struct{}{}
                result = append(result, item)
            }
        }
        return result, nil
    case []int64:
        result := make([]int64, 0, len(originals.([]int64)))
        for _, item := range slice {
            key := fmt.Sprint(item)
            if _, ok := temp[key]; !ok {
                temp[key] = struct{}{}
                result = append(result, item)
            }
        }
        return result, nil
    default:
        err := Errorf("Unknown type: %T", slice)
        return nil, err
    }
}
  • 函数接收一个空接口类型的参数,然后使用类型选择进入相应的分支进行处理。这里可以根据需求添加函数需支持的切片类型的处理程序。
  • 每个分支里同样创建了一个key类型为string值类型为空 structmap 。key的值是切片元素的字符串表现形式(类型的 String() 方法的返回值)
  • 函数返回值的类型是空接口,所以拿到返回值后要进行类型断言才能使用。

原文链接


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK