Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slice过滤重复元素 #5

Open
kevinyan815 opened this issue Aug 4, 2019 · 1 comment
Open

Slice过滤重复元素 #5

kevinyan815 opened this issue Aug 4, 2019 · 1 comment

Comments

@kevinyan815
Copy link
Owner

kevinyan815 commented Aug 4, 2019

Slice去除重复元素

golang标准库本身没有提供一个去除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不占内存空间,使用它来实现我们的函数空间复杂度是最低的。

Playground URL
Reference URL

适配多个切片类型

上面的去除重复元素的函数,只能处理字符串切片对于其他类型的切片就不行了。如果不想针对每种类型的切片都写一个去重函数的话可以使用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()方法的返回值)

  • 函数返回值的类型是空接口,所以拿到返回值后要进行类型断言才能使用。

@yuemingming
Copy link

func distinct[T comparable](input []T) []T {
	set := make(map[T]struct{})
	res := make([]T, 0, len(set))
	for _, value := range input {
		if _, ok := set[value]; !ok {
			set[value] = struct{}{}
			res = append(res, value)
		}
	}
	return res
}

提供一个泛型实现

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants