Golang의 슬라이스에서 요소를 삭제하는 방법


111
fmt.Println("Enter position to delete::")
fmt.Scanln(&pos)

new_arr := make([]int, (len(arr) - 1))
k := 0
for i := 0; i < (len(arr) - 1); {
    if i != pos {
        new_arr[i] = arr[k]
        k++
        i++
    } else {
        k++
    }
}

for i := 0; i < (len(arr) - 1); i++ {
    fmt.Println(new_arr[i])
}

슬라이스에서 요소를 삭제하기 위해이 명령을 사용하고 있지만 작동하지 않습니다. 제안 해주세요.


2
이것은 좋은 읽기입니다 : blog.golang.org/go-slices-usage-and-internals
squiguy

답변:


178

주문 문제

배열의 순서를 유지하려면 삭제 인덱스 오른쪽에있는 모든 요소를 ​​왼쪽으로 하나씩 이동해야합니다. 바라건대, 이것은 Golang에서 쉽게 할 수 있습니다 :

func remove(slice []int, s int) []int {
    return append(slice[:s], slice[s+1:]...)
}

그러나 이는 비용이 많이 드는 모든 요소를 ​​이동하게 될 수 있기 때문에 비효율적입니다.

순서는 중요하지 않습니다

순서 지정에 신경 쓰지 않는다면, 삭제할 요소를 슬라이스 끝의 요소로 교체 한 다음 n-1 개의 첫 번째 요소를 반환 할 수있는 훨씬 빠른 가능성이 있습니다.

func remove(s []int, i int) []int {
    s[len(s)-1], s[i] = s[i], s[len(s)-1]
    return s[:len(s)-1]
}

reslicing 방법을 사용하면 1000000 요소의 배열을 비우는 데 224 초가 걸리고이 방법은 0.06ns 만 걸립니다. 내부적으로 go는 수정하지 않고 슬라이스의 길이 만 변경한다고 생각합니다.

편집 1

아래 주석을 기반으로 한 빠른 메모 (감사합니다!).

목적이 요소를 삭제하는 것이므로 순서가 중요하지 않은 경우 단일 스왑이 필요하면 두 번째 스왑이 낭비됩니다.

func remove(s []int, i int) []int {
    s[i] = s[len(s)-1]
    // We do not need to put s[i] at the end, as it will be discarded anyway
    return s[:len(s)-1]
}

또한이 답변은 경계 검사를 수행하지 않습니다. . 입력으로 유효한 색인이 필요합니다. 즉, len (s)보다 크거나 같은 음수 값이나 인덱스는 Go를 패닉 상태로 만듭니다. 슬라이스와 배열이 0 인덱싱되어 배열의 n 번째 요소를 제거하면 입력 n-1 을 제공함을 의미합니다 . 첫번째 요소 콜 제거 제거 (S, 0) , 제 콜 제거 제거 (S 1) , 및 기타 등등.


20
원본 조각을 보존하는 데 신경 쓰지 않으면 교체가 필요하지 않고 s[i] = s[len(s)-1]; return s[:len(s)-1]충분할 것 같습니다.
브래드 피바디

@bgp이 그냥 터지는 하지 원래의 질문에 제공 인덱스에서 하나를 제거, 슬라이스의 (마지막 요소를 제거). 마찬가지로 원래 질문에 대답하지 않는 이동 (첫 번째 요소 제거)을 return s[1:]사용할 수 있습니다.
shadyyx

2
흠,별로. 이것은 s[i] = s[len(s)-1]확실히 마지막 요소를 index의 요소에 복사합니다 i. 그런 다음 return s[:len(s)-1]마지막 요소가없는 슬라이스를 반환합니다. 거기에 두 가지 진술이 있습니다.
Brad Peabody

1
len (arr) == 2로 실패하고 삭제할 요소가 마지막 항목입니다. play.golang.org/p/WwD4PfUUjsM
zenocon

1
@zenocon Golang에서 배열은 0 인덱스입니다. 즉, 길이 2의 배열에 대한 유효한 인덱스는 0과 1입니다. 실제로이 함수는 배열의 경계를 확인하지 않으며 유효한 인덱스가 제공 될 것으로 예상합니다. 경우 LEN (도착) == 2 유효한 인자가 포함된다 0 또는 1 . 다른 모든 것이 경계를 벗어난 액세스를 트리거하고 Go는 패닉 상태가됩니다.
T. Claverie

41

슬라이스에서 하나의 요소를 제거합니다 (이를 '재 슬라이싱'이라고 함).

package main

import (
    "fmt"
)

func RemoveIndex(s []int, index int) []int {
    return append(s[:index], s[index+1:]...)
}

func main() {
    all := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    fmt.Println(all) //[0 1 2 3 4 5 6 7 8 9]
    all = RemoveIndex(all, 5)
    fmt.Println(all) //[0 1 2 3 4 6 7 8 9]
}

8
이를 '재 슬라이싱'이라고하며 Go에서이 작업을 수행하는 관용적 인 방법이지만 상대적으로 비용이 많이 듭니다. 연결 목록에서 노드를 제거하는 것과 같은 작업과 혼동하지 마십시오. 이는 그렇지 않기 때문에 특히 대규모 컬렉션에서 많은 작업을 수행하려는 경우이를 피하는 대체 디자인을 고려해야합니다.
evanmcdonnal

예, 이것은 Golang의 관용적 방법이며 C / Assembly에서도 정렬 된 배열에서 임의의 요소 하나를 제거하는 것은 비용이 많이 들지만 모든 오른쪽 요소를 왼쪽으로 한 위치 이동 (복사)해야합니다. 예 일부 사용 사례에서는 연결된 목록이 목록에서 임의의 요소를 제거하는 더 나은 솔루션입니다.

1
이 메서드는 모두 수정되고 이제 n과 모두 동일한 기본 배열의 일부를 가리 킵니다. 이로 인해 코드에 버그가 발생할 가능성이 큽니다.
mschuett

1
이 오류를 얻기2019/09/28 19:46:25 http: panic serving 192.168.1.3:52817: runtime error: slice bounds out of range [7:5] goroutine 7 [running]:
OhhhThatVarun

10
이것은 panic인덱스가 슬라이스의 마지막 요소 인 경우 발생 합니다
STEEL

28

마이너 포인트 (코드 골프)이지만, 순서가 중요하지 않은 경우 값을 바꿀 필요가 없습니다. 제거되는 배열 위치를 마지막 위치의 복제본으로 덮어 쓴 다음 잘린 배열을 반환하십시오.

func remove(s []int, i int) []int {
    s[i] = s[len(s)-1]
    return s[:len(s)-1]
}

같은 결과.


13
가장 읽기 쉬운 구현은 첫 번째 요소를 지정된 index에 복사 s[i] = s[0]한 다음 마지막 n-1 요소 만있는 배열을 반환하는 것입니다. return s[1:]
Kent

4
놀이터 @Kent의 솔루션
stevenspiel

2
@Kent와 s[1:]비교 하여 문제 s[:len(s)-1]는 나중에 슬라이스가 append편집되거나 삭제가 appends 와 섞이면 나중에 훨씬 더 잘 수행된다는 것 입니다. 나중은 슬라이스 용량을 유지하지만 전자는 그렇지 않습니다.
Dave C

1
이 함수로 0 번째 요소를 제거하면 결과가 반전됩니다.
ivarec

21

보기에 약간 이상하지만 여기에있는 대부분의 답변은 위험하고 실제로하는 일에 대해 광택이 없습니다. 조각에서 항목을 제거하는 것에 대해 묻는 원래 질문을 보면 조각의 복사본이 만들어지고 채워지고 있습니다. 이렇게하면 슬라이스가 프로그램 주위로 전달 될 때 미묘한 버그가 발생하지 않습니다.

다음은이 스레드와 원본 게시물의 사용자 답변을 비교하는 코드입니다. 이 코드를 엉망으로 만드는 이동 놀이터 가 있습니다.

추가 기반 제거

package main

import (
    "fmt"
)

func RemoveIndex(s []int, index int) []int {
    return append(s[:index], s[index+1:]...)
}

func main() {
    all := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    fmt.Println("all: ", all) //[0 1 2 3 4 5 6 7 8 9]
    removeIndex := RemoveIndex(all, 5)

    fmt.Println("all: ", all) //[0 1 2 3 4 6 7 8 9 9]
    fmt.Println("removeIndex: ", removeIndex) //[0 1 2 3 4 6 7 8 9]

    removeIndex[0] = 999
    fmt.Println("all: ", all) //[999 1 2 3 4 6 7 9 9]
    fmt.Println("removeIndex: ", removeIndex) //[999 1 2 3 4 6 7 8 9]
}

위의 예에서 슬라이스를 만들고 0부터 9까지의 숫자로 수동으로 채울 수 있습니다. 그런 다음 모든 인덱스에서 인덱스 5를 제거하고 인덱스를 제거하도록 할당합니다. 그러나 우리가 지금 모든 것을 인쇄 할 때 우리는 그것이 수정 된 것을 볼 수 있습니다. 슬라이스가 기본 배열에 대한 포인터이기 때문입니다. 에서 더 이상 도달 할 수없는 한 요소에 의해 차이 가 더 길어 지면서 수정되는 removeIndex원인도 작성합니다 . 다음으로 값을 변경 하면 수정되는 것도 볼 수 있습니다. 효과적인 이동allallremoveIndexremoveIndexall 은 이것에 대해 더 자세히 설명합니다.

다음 예제는 다루지 않겠지 만 우리의 목적을 위해 동일한 작업을 수행합니다. 그리고 복사를 사용하는 것이 다르지 않다는 것을 보여줍니다.

package main

import (
    "fmt"
)

func RemoveCopy(slice []int, i int) []int {
    copy(slice[i:], slice[i+1:])
    return slice[:len(slice)-1]
}

func main() {
    all := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    fmt.Println("all: ", all) //[0 1 2 3 4 5 6 7 8 9]
    removeCopy := RemoveCopy(all, 5)

    fmt.Println("all: ", all) //[0 1 2 3 4 6 7 8 9 9]
    fmt.Println("removeCopy: ", removeCopy) //[0 1 2 3 4 6 7 8 9]

    removeCopy[0] = 999
    fmt.Println("all: ", all) //[99 1 2 3 4 6 7 9 9]
    fmt.Println("removeCopy: ", removeCopy) //[999 1 2 3 4 6 7 8 9]
}

질문 원래 답변

원래 질문을 보면 항목을 제거하는 조각이 수정되지 않습니다. 이 스레드의 원래 답변은 지금까지이 페이지를 찾는 대부분의 사람들에게 가장 적합합니다.

package main

import (
    "fmt"
)

func OriginalRemoveIndex(arr []int, pos int) []int {
    new_arr := make([]int, (len(arr) - 1))
    k := 0
    for i := 0; i < (len(arr) - 1); {
        if i != pos {
            new_arr[i] = arr[k]
            k++
        } else {
            k++
        }
        i++
    }

    return new_arr
}

func main() {
    all := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    fmt.Println("all: ", all) //[0 1 2 3 4 5 6 7 8 9]
    originalRemove := OriginalRemoveIndex(all, 5)

    fmt.Println("all: ", all) //[0 1 2 3 4 5 6 7 8 9]
    fmt.Println("originalRemove: ", originalRemove) //[0 1 2 3 4 6 7 8 9]

    originalRemove[0] = 999
    fmt.Println("all: ", all) //[0 1 2 3 4 5 6 7 8 9]
    fmt.Println("originalRemove: ", originalRemove) //[999 1 2 3 4 6 7 8 9]
}

보시다시피이 출력은 대부분의 사람들이 기대하고 대부분의 사람들이 원하는대로 작동합니다. 수정은 변경을 originalRemove일으키지 않으며 all인덱스를 제거하고 할당하는 작업도 변경을 일으키지 않습니다! 환상적입니다!

이 코드는 약간 길지만 위의 내용을 이것으로 변경할 수 있습니다.

정답

package main

import (
    "fmt"
)

func RemoveIndex(s []int, index int) []int {
    ret := make([]int, 0)
    ret = append(ret, s[:index]...)
    return append(ret, s[index+1:]...)
}

func main() {
    all := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    fmt.Println("all: ", all) //[0 1 2 3 4 5 6 7 8 9]
    removeIndex := RemoveIndex(all, 5)

    fmt.Println("all: ", all) //[0 1 2 3 4 5 6 7 8 9]
    fmt.Println("removeIndex: ", removeIndex) //[0 1 2 3 4 6 7 8 9]

    removeIndex[0] = 999
    fmt.Println("all: ", all) //[0 1 2 3 4 5 6 7 9 9]
    fmt.Println("removeIndex: ", removeIndex) //[999 1 2 3 4 6 7 8 9]
}

원래 인덱스 제거 솔루션과 거의 동일하지만 반환하기 전에 추가 할 새 슬라이스를 만듭니다.


3
이것은 슬라이스의 백업 배열을 수정하는 위험을 설명하는 유일한 것이기 때문에 질문에 대한 답이되어야합니다
JessG

추가 기반 제거에서는 추가가 충분한 용량이있는 경우 동일한 기본 어레이를 재사용하기 때문에 첫 번째 슬라이스 all도 변경 removeIndex := RemoveIndex(all, 5)됩니다 (물론 요소 제거에는 더 많은 용량이 필요하지 않음). 반대로 요소를 추가하고에 할당하면 removeIndexappend는 새 배열을 할당하고 all변경되지 않습니다.
blackgreen

11

이것이 golang 방식으로 슬라이스 에서 삭제 하는 방법 입니다. append에 내장 된 함수를 빌드 할 필요가 없습니다. 여기에서 시도하십시오 https://play.golang.org/p/QMXn9-6gU5P

z := []int{9, 8, 7, 6, 5, 3, 2, 1, 0}
fmt.Println(z)  //will print Answer [9 8 7 6 5 3 2 1 0]

z = append(z[:2], z[4:]...)
fmt.Println(z)   //will print Answer [9 8 5 3 2 1 0]

8

The Go Programming Language 책에서

슬라이스 중간에서 요소를 제거하고 나머지 요소의 순서를 유지하려면 copy를 사용하여 더 높은 번호의 요소를 아래로 밀어 간격을 채 웁니다.

func remove(slice []int, i int) []int {
  copy(slice[i:], slice[i+1:])
  return slice[:len(slice)-1]
}

3
이 메서드는 전달 된 원본 조각이 수정되도록합니다.
mschuett

7

아래 방법을 사용하여 항목을 조각으로 제거합니다. 이것은 다른 사람들의 가독성에 도움이됩니다. 또한 불변합니다.

func remove(items []string, item string) []string {
    newitems := []string{}

    for _, i := range items {
        if i != item {
            newitems = append(newitems, i)
        }
    }

    return newitems
}

이 접근 방식이 실제로 항목의 모든 항목을 제거하는 것이 더 좋습니다.
eexit

이것은 슬라이스에서 여러 항목을 필터링하도록 쉽게 변환 할 수 있습니다.
eldad 부과

1

이 방법을 시도해 볼 수 있습니다.

// DelEleInSlice delete an element from slice by index
//  - arr: the reference of slice
//  - index: the index of element will be deleted
func DelEleInSlice(arr interface{}, index int) {
    vField := reflect.ValueOf(arr)
    value := vField.Elem()
    if value.Kind() == reflect.Slice || value.Kind() == reflect.Array {
        result := reflect.AppendSlice(value.Slice(0, index), value.Slice(index+1, value.Len()))
        value.Set(result)
    }
}

용법:

arrInt := []int{0, 1, 2, 3, 4, 5}
arrStr := []string{"0", "1", "2", "3", "4", "5"}
DelEleInSlice(&arrInt, 3)
DelEleInSlice(&arrStr, 4)
fmt.Println(arrInt)
fmt.Println(arrStr)

결과:

0, 1, 2, 4, 5
"0", "1", "2", "3", "5"

1
질문이 묻는 것에 대해 관용적이고 과도하게 설계되지 않았기 때문일 수 있습니다. 그것은 그것을 해결하는 흥미로운 방법이지만 아무도 이것을 사용해서는 안됩니다.
mschuett

1
감사! 사실이 어쩌면, 보편적 인 것 같다 있기 때문에, 인터페이스와 나를 위해 잘 일했다
DADi590

1

아마도이 코드가 도움이 될 것입니다.

주어진 인덱스가있는 항목을 삭제합니다.

배열과 인덱스를 가져 와서 추가 함수와 매우 유사한 새 배열을 반환합니다.

func deleteItem(arr []int, index int) []int{
  if index < 0 || index >= len(arr){
    return []int{-1}
  }

    for i := index; i < len(arr) -1; i++{
      arr[i] = arr[i + 1]

    }

    return arr[:len(arr)-1]
}

여기에서 코드로 플레이 할 수 있습니다 : https://play.golang.org/p/aX1Qj40uTVs


1

가장 좋은 방법은 추가 기능을 사용하는 것입니다.

package main

import (
    "fmt"
)

func main() {
    x := []int{4, 5, 6, 7, 88}
    fmt.Println(x)
    x = append(x[:2], x[4:]...)//deletes 6 and 7
    fmt.Println(x)
}

https://play.golang.org/p/-EEFCsqse4u


1

재배치하지 않고 여기 에서 방법을 찾으십시오 .

  • 순서 변경
a := []string{"A", "B", "C", "D", "E"}
i := 2

// Remove the element at index i from a.
a[i] = a[len(a)-1] // Copy last element to index i.
a[len(a)-1] = ""   // Erase last element (write zero value).
a = a[:len(a)-1]   // Truncate slice.

fmt.Println(a) // [A B E D]
  • 질서를 지키다
a := []string{"A", "B", "C", "D", "E"}
i := 2

// Remove the element at index i from a.
copy(a[i:], a[i+1:]) // Shift a[i+1:] left one index.
a[len(a)-1] = ""     // Erase last element (write zero value).
a = a[:len(a)-1]     // Truncate slice.

fmt.Println(a) // [A B D E]

0

내용을 신경 쓰지 않는 한 모든 요소를 ​​확인할 필요가 없으며 슬라이스 추가를 활용할 수 있습니다. 그것을 시도

pos := 0
arr := []int{1, 2, 3, 4, 5, 6, 7, 9}
fmt.Println("input your position")
fmt.Scanln(&pos)
/* you need to check if negative input as well */
if (pos < len(arr)){
    arr = append(arr[:pos], arr[pos+1:]...)
} else {
    fmt.Println("position invalid")
}

-1

여기에 포인터가있는 놀이터 예제가 있습니다. https://play.golang.org/p/uNpTKeCt0sH

package main

import (
    "fmt"
)

type t struct {
    a int
    b string
}

func (tt *t) String() string{
    return fmt.Sprintf("[%d %s]", tt.a, tt.b)
}

func remove(slice []*t, i int) []*t {
  copy(slice[i:], slice[i+1:])
  return slice[:len(slice)-1]
}

func main() {
    a := []*t{&t{1, "a"}, &t{2, "b"}, &t{3, "c"}, &t{4, "d"}, &t{5, "e"}, &t{6, "f"}}
    k := a[3]
    a = remove(a, 3)
    fmt.Printf("%v  ||  %v", a, k)
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.