前往:如果唯一则追加


77

有没有一种方法可以检查切片/映射中是否存在值?

只想在切片中存在值时才将其添加到切片中。

这可行,但似乎很冗长。有更好的方法吗?

orgSlice := []int{1, 2, 3}
newSlice := []int{}
newInt := 2

newSlice = append(newSlice, newInt)
for _, v := range orgSlice {
    if v != newInt {
        newSlice = append(newSlice, v)
    }
}

newSlice == [2 1 3]

1
Re:EDIT-任何有效的地图键类型都是相同的故事-字符串是。
zzzz 2012年

1
Re:EDIT2-如果'newSlice'中的值顺序无关紧要,并且将使用range语句使用/使用它,那么它的构造是多余的-只需对'set'的键进行范围调整即可。
zzzz 2012年

@jnml感谢您的评论。我ints在GAE数据存储区中存储的列表,并且出于查询目的,该列表必须为slice([]int)。该要求是否使我的初始技术成为更好的选择?列表将很小。
凯尔·芬利

3
您可以append()通过newslice := make([]int, len(set))首先创建一个来避免使用(和所有重新分配)。如果您进行了大量这样的“包含键...”测试(至少超过2个),则将切片转换为map [int] struct {}可能会更快,如果仅执行几次,则循环直接通过切片可能更好。
tux21b

@ tux21b好的,谢谢,非常感谢您抽出宝贵的时间来解释所有这些内容。
凯尔·芬利

Answers:


111

您的方法将为每次插入花费线性时间。更好的方法是使用map[int]struct{}。另外,您也可以使用amap[int]bool或类似名称,但是emptystruct{}的优点是它不会占用任何额外的空间。因此,map[int]struct{}是一组整数的普遍选择。

例:

set := make(map[int]struct{})
set[1] = struct{}{}
set[2] = struct{}{}
set[1] = struct{}{}
// ...

for key := range(set) {
  fmt.Println(key)
}
// each value will be printed only once, in no particular order


// you can use the ,ok idiom to check for existing keys
if _, ok := set[1]; ok {
  fmt.Println("element found")
} else {
  fmt.Println("element not found")
}

1
谢谢您的回复。几个问题:您将如何重新创建切片?有没有办法使该策略适用于字符串?很抱歉,如果这些很明显-我是Go的新手。
凯尔·芬利

4
我将在计算过程中仅使用地图(因为它具有O(1)行为而不是O(n))。之后,您可以创建一个切片并复制地图中的每个值。之后,元素将具有随机顺序,因此您可能需要对其进行排序。您可以将int,float,struct,string和数组用作映射键(至少在Go1中使用)。
tux21b'2

1
特别感谢您概述了空结构不会占用额外的空间。我不知道这一点,会使用map [type] interface {}并将nil分配给该接口。
user1943442 15/10/27

我也使用了map [type] interface {}方法,这也不会占用额外的空间吗?
威廉·金

42

如果找不到,最有效的方法可能是遍历切片并追加。

func AppendIfMissing(slice []int, i int) []int {
    for _, ele := range slice {
        if ele == i {
            return slice
        }
    }
    return append(slice, i)
}

它简单明了,对于小清单来说很快。

而且,它总是比您当前的基于地图的解决方案快。无论如何,基于地图的解决方案都会迭代整个切片。当发现新值已经存在时,此解决方案立即返回。两种解决方案在迭代时都比较元素。(每个映射赋值语句肯定在内部至少进行了一个映射键比较。)仅当您可以在许多插入中维护它时,映射才有用。如果您在每次插入时都对其进行重建,则将失去所有优势。

如果您确实需要有效处理大型列表,请考虑按排序顺序维护列表。(我怀疑顺序对您来说无关紧要,因为您的第一个解决方案附加在列表的开头,而最新的解决方案附加在列表的末尾。)如果您始终对列表进行排序,则可以使用sort.Search函数进行有效的二进制插入。


20
“无论如何,基于地图的解决方案都会遍历整个切片”-您确定这就是哈希地图的工作方式吗?
Ottokar

@Ottokar,他错了吗?许多人支持,但没有任何回应。
Filip Bartuzi '17

8
@FilipBartuzi实际上,我认为我可能误解了该声明的含义。哈希映射显然不会在元素上进行迭代以找到键,但是如果我们必须将切片转换为地图然后将地图转换回切片,则“基于地图的解决方案”“如果唯一则追加到切片”将失去优势。 。
奥托卡(Ottokar)


0

区分结构数组:

func distinctObjects(objs []ObjectType) (distinctedObjs [] ObjectType){
        var output []ObjectType
    for i:= range objs{
        if output==nil || len(output)==0{
            output=append(output,objs[i])
        } else {
            founded:=false
            for j:= range output{
                    if output[j].fieldname1==objs[i].fieldname1 && output[j].fieldname2==objs[i].fieldname2 &&......... {
                    founded=true
                }
            }
            if !founded{
                output=append(output,objs[i])
            }
        }
    }
    return output
}

这里的结构是这样的:

type ObjectType struct {
    fieldname1 string
    fieldname2 string
    .........
}

该对象将通过此处的选中字段来区分:

if output[j].fieldname1==objs[i].fieldname1 && output[j].fieldname2==objs[i].fieldname2 &&......... {

-1
package main

import (
    "fmt"
    "os"
    "reflect"
)

func main() {
/*  s := []string{"a", "b"}
    fmt.Println(s)

    s = AppendIfMissing(s, "4").([]string)

    fmt.Println(s)*/

/*  var a []*string
    a = make([]*string, 0)
    e := "4"
    a = AppendIfMissing(a, &e).([]*string)
    fmt.Println(*a[0])*/

    var a []*float64
    a = make([]*float64, 3)
    e := 4.4
    d := 4.41
    a = AppendIfMissing(a, &e).([]*float64)
    a = AppendIfMissing(a, &d).([]*float64)
    fmt.Println(*a[3], *a[4])
}

func AppendIfMissing(array interface{}, element interface{}) interface{} {
    if reflect.ValueOf(array).IsNil() {
        fmt.Fprintf(os.Stderr, "array not initialized\n")
        return nil
    }

    switch reflect.TypeOf(array).Kind() {
    case reflect.Slice:
        arrayV := reflect.ValueOf(array)
        arrayVLen := arrayV.Len()
        if arrayVLen == 0 {//if make len == 0
            sliceNew := reflect.MakeSlice(reflect.ValueOf(array).Type(), 1, 1)
            if sliceNew.Index(0).Type() != reflect.ValueOf(element).Type() {
                fmt.Fprintf(os.Stderr, "types are not same\n")
                return sliceNew.Interface()
            }

            sliceNew.Index(0).Set(reflect.ValueOf(element))
            return sliceNew.Interface()
        }
        for i := 0; i < arrayVLen; i++ {
            if i == 0 && reflect.ValueOf(element).Kind() != arrayV.Index(i).Kind() {
                fmt.Fprintf(os.Stderr, "types are not same\n")
                return array
            }
            if arrayV.Index(i).Interface() == element {
                return array
            }
        }
    default:
        fmt.Fprintf(os.Stderr, "first element is not array\n")
        return array
    }

    arrayV := reflect.ValueOf(array)
    elementV := reflect.ValueOf(element)
    appendAE := reflect.Append(arrayV, elementV)

    return appendAE.Interface()
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.