Go: Добавить, если уникальный - PullRequest
49 голосов
/ 12 февраля 2012

Есть ли способ проверить фрагменты / карты на наличие значения?

Я хотел бы добавить значение к срезу только , если оно не существует в срезе.

Это работает, но кажется многословным. Есть ли лучший способ сделать это?

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]

Ответы [ 4 ]

75 голосов
/ 12 февраля 2012

Ваш подход будет занимать линейное время для каждой вставки.Лучше было бы использовать map[int]struct{}.В качестве альтернативы, вы также можете использовать map[int]bool или что-то подобное, но пустое struct{} имеет то преимущество, что оно не занимает никакого дополнительного пространства.Поэтому 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")
}
32 голосов
/ 05 марта 2012

Наиболее эффективным, вероятно, будет итерация по срезу и добавление, если вы его не найдете.

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

Это просто и очевидно и быстро для небольших списков.

Кроме того, это всегда будет быстрее, чем ваше текущее решение на основе карт.Основанное на карте решение перебирает весь срез независимо от того, что;это решение немедленно возвращается, когда обнаруживает, что новое значение уже присутствует.Оба решения сравнивают элементы по мере их повторения.(Каждый оператор присваивания карты, безусловно, выполняет по крайней мере одно внутреннее сравнение ключей карты.) Карта была бы полезна, только если вы могли бы поддерживать ее во многих вставках.Если вы перестраиваете его при каждой вставке, все преимущества теряются.

Если вам действительно нужно эффективно обрабатывать большие списки, рассмотрите возможность сохранения списков в отсортированном порядке.(Я подозреваю, что порядок не имеет значения для вас, потому что ваше первое решение добавляется в начале списка, а ваше последнее решение добавляется в конце.) Если вы всегда сохраняете списки отсортированными, то вы можете использовать функцию sort.Search дляделать эффективные двоичные вставки.

0 голосов
/ 23 января 2019
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()
}
0 голосов
/ 15 ноября 2018

различая массив структуры:

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 &&......... {
...