68

Given

array1 := []int{1, 3, 4, 5}
array2 := []int{2, 4, 6, 8}

I want to insert array2[2] i.e 6 at array1[1] i.e before 3 so that array1 becomes a slice of {1, 6, 3, 4, 5}. How can I do it?

Most the techniques I read online involve using the : operator but results in remaining elements being inserted as well. How can I append single values at an index in a slice?

wasmup
  • 14,541
  • 6
  • 42
  • 58
chefcurry7
  • 4,813
  • 11
  • 28
  • 33

12 Answers12

100

A simple append is what you need:

a = append(a[:index+1], a[index:]...)
a[index] = value

Note: len(a) > 0 && index < len(a)

Should len(a) == index, meaning nil or empty slice or append after the last element:

a = append(a, value)

Inserting at the index zero for slice of ints:

a = append([]int{value}, a...)

All in one function:

// 0 <= index <= len(a)
func insert(a []int, index int, value int) []int {
    if len(a) == index { // nil or empty slice or after last element
        return append(a, value)
    }
    a = append(a[:index+1], a[index:]...) // index < len(a)
    a[index] = value
    return a
}

Usage:

    a := []int{10, 30, 40}
    a = insert(a, 1, 20)
    fmt.Println(a) // [10 20 30 40]

And for the OP:

    slice1 := []int{1, 3, 4, 5}
    slice2 := []int{2, 4, 6, 8}
    // slice1 = insert(slice1, 1, slice2[2])
    slice1 = append(slice1[:2], slice1[1:]...)
    slice1[1] = slice2[2]

    fmt.Println(slice1) // [1 6 3 4 5]

Benchmark:

go version
# go version go1.16.3 linux/amd64
make bench
go test -benchmem -bench . -args -n 32
# BenchmarkInsert-8      4125085  275.0 ns/op  512 B/op  1 allocs/op
# BenchmarkInsert2-8     3778551  316.0 ns/op  512 B/op  1 allocs/op

go test -benchmem -bench . -args -n 1000
# BenchmarkInsert-8       198364  5876 ns/op  16384 B/op  1 allocs/op
# BenchmarkInsert2-8      205197  7123 ns/op  16384 B/op  1 allocs/op

go test -benchmem -bench . -args -n 1000000
# BenchmarkInsert-8          643  1898436 ns/op 10002437 B/op  1 allocs/op
# BenchmarkInsert2-8         368  3248385 ns/op 10002436 B/op  1 allocs/op

Code:

func insert(a []int, index int, value int) []int {
    a = append(a[:index+1], a[index:]...) // Step 1+2
    a[index] = value                      // Step 3
    return a
}
func insert2(a []int, index int, value int) []int {
    last := len(a) - 1
    a = append(a, a[last])           // Step 1
    copy(a[index+1:], a[index:last]) // Step 2
    a[index] = value                 // Step 3
    return a
}
func BenchmarkInsert(b *testing.B) {
    for i := 0; i < b.N; i++ {
        r = insert(a, 2, 42)
    }
}
func BenchmarkInsert2(b *testing.B) {
    for i := 0; i < b.N; i++ {
        r = insert2(a, 2, 42)
    }
}

var (
    n    = flag.Int("n", 32, "buffer length")
    a, r []int
)

// We use TestMain to set up the buffer.
func TestMain(m *testing.M) {
    flag.Parse()
    a = make([]int, *n)
    os.Exit(m.Run())
}

You may combine the two first steps to one; by using:

    a = append(a[:index+1], a[index:]...)
  1. This makes sure the array has enough capacity to accommodate the new element.
  2. This copies all required elements to one index higher to make room for the new element.
  3. Set the element at the index, using a single assignment: a[index] = value

Which is more efficient, according to the benchmarks.


Should you need to insert at index > cap(a) (note: untested code) - try it on the The Go Playground:

package main

import "fmt"

func insert(a []int, index int, value int) []int {
    n := len(a)
    if index < 0 {
        index = (index%n + n) % n
    }
    switch {
    case index == n: // nil or empty slice or after last element
        return append(a, value)

    case index < n: // index < len(a)
        a = append(a[:index+1], a[index:]...)
        a[index] = value
        return a

    case index < cap(a): // index > len(a)
        a = a[:index+1]
        for i := n; i < index; i++ {
            a[i] = 0
        }
        a[index] = value
        return a

    default:
        b := make([]int, index+1) // malloc
        if n > 0 {
            copy(b, a)
        }
        b[index] = value
        return b
    }
}

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

    a = []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    a = a[:4]
    fmt.Println(insert(a, 5, 50)) // [0 1 2 3 0 50]

    fmt.Println(insert(make([]int, 0, 50), 10, 10)) // [0 0 0 0 0 0 0 0 0 0 10]

    fmt.Println(insert([]int{1}, -1, 10))       // [10 1]
    fmt.Println(insert([]int{1, 2, 3}, -2, 10)) // [1 10 2 3]
    fmt.Println(insert([]int{1, 2, 3}, -1, 10)) // [1 2 10 3]
    fmt.Println(insert([]int{1, 2, 3}, -4, 10)) // [1 2 10 3]

    fmt.Println(insert(nil, 0, 0))       // [0]
    fmt.Println(insert([]int{}, 0, 0))   // [0]
    fmt.Println(insert([]int{1}, 1, 10)) // [1 10]

    fmt.Println(insert(nil, 5, 50)) // [0 0 0 0 0 50]

    fmt.Println(insert(make([]int, 0, 1), 1, 10)) // [0 10]
    fmt.Println(insert(make([]int, 0, 1), 2, 20)) // [0 0 20]
    fmt.Println(insert(make([]int, 0, 1), 3, 30)) // [0 0 0 30]

    fmt.Println(insert([]int{0, 10, 20, 30}, 5, 50)) // [0 10 20 30 0 50]
    fmt.Println(insert([]int{0}, 5, 50))             // [0 0 0 0 0 50]
    fmt.Println(insert([]int{0, 10}, 0, 0))          // [0 0 10]
    fmt.Println(insert([]int{0, 10}, 1, 5))          // [0 5 10]
    fmt.Println(insert(make([]int, 5, 50), 0, 0))    // [0 0 0 0 0 0]
}

Using generics (note: untested code) - try it on the The Go Playground:

package main

import "fmt"

func insert[T any](a []T, index int, value T) []T {
    n := len(a)
    if index < 0 {
        index = (index%n + n) % n
    }
    switch {
    case index == n: // nil or empty slice or after last element
        return append(a, value)

    case index < n: // index < len(a)
        a = append(a[:index+1], a[index:]...)
        a[index] = value
        return a

    case index < cap(a): // index > len(a)
        a = a[:index+1]
        var zero T
        for i := n; i < index; i++ {
            a[i] = zero
        }
        a[index] = value
        return a

    default:
        b := make([]T, index+1) // malloc
        if n > 0 {
            copy(b, a)
        }
        b[index] = value
        return b
    }
}

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

    a = []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    a = a[:4]
    fmt.Println(insert(a, 5, 50)) // [0 1 2 3 0 50]

    fmt.Println(insert(make([]int, 0, 50), 10, 10)) // [0 0 0 0 0 0 0 0 0 0 10]

    fmt.Println(insert([]int{1}, -1, 10))       // [10 1]
    fmt.Println(insert([]int{1, 2, 3}, -2, 10)) // [1 10 2 3]
    fmt.Println(insert([]int{1, 2, 3}, -1, 10)) // [1 2 10 3]
    fmt.Println(insert([]int{1, 2, 3}, -4, 10)) // [1 2 10 3]

    fmt.Println(insert(nil, 0, 0))       // [0]
    fmt.Println(insert([]int{}, 0, 0))   // [0]
    fmt.Println(insert([]int{1}, 1, 10)) // [1 10]

    fmt.Println(insert(nil, 5, 50)) // [0 0 0 0 0 50]

    fmt.Println(insert(make([]int, 0, 1), 1, 10)) // [0 10]
    fmt.Println(insert(make([]int, 0, 1), 2, 20)) // [0 0 20]
    fmt.Println(insert(make([]int, 0, 1), 3, 30)) // [0 0 0 30]

    fmt.Println(insert([]int{0, 10, 20, 30}, 5, 50)) // [0 10 20 30 0 50]
    fmt.Println(insert([]int{0}, 5, 50))             // [0 0 0 0 0 50]
    fmt.Println(insert([]int{0, 10}, 0, 0))          // [0 0 10]
    fmt.Println(insert([]int{0, 10}, 1, 5))          // [0 5 10]
    fmt.Println(insert(make([]int, 5, 50), 0, 0))    // [0 0 0 0 0 0]
}
wasmup
  • 14,541
  • 6
  • 42
  • 58
  • 10
    This should be the accepted solution. It is the most succinct solution and it comes with benchmarks. – Steven Eckhoff Aug 18 '20 at 17:15
  • I'm not sure if this is by design, but neither `insert` nor `insert2` can be used to append an item at the end of the slice. Trying to do so generates a panic: https://go.dev/play/p/zW32Fbf1Nxi – Daniel Yankowsky Jan 05 '23 at 18:21
  • @DanielYankowsky You need to run benchmark in a file named `main_test.go` and for the main only use the "All in one function" - and please read the top section. – wasmup Jan 05 '23 at 20:43
  • @wasmup Yes, I saw that you had a note about inserting into a nil slice or at the length of the slice, but it wasn't clear to me what that note was trying to say. I guess you were saying "is not supported". That seems like a pretty glaring limitation. – Daniel Yankowsky Jan 06 '23 at 04:16
  • 1
    @DanielYankowsky Check the generics version at the end of this new edit, I hope this helps. [Run it](https://go.dev/play/p/dS9bHFuqCbI) – wasmup Jan 06 '23 at 07:59
46

Simple, efficient and logical way:

  1. Make sure array1 has enough capacity (length) to accomodate the new, insertable element. To do that, append a single element using the builting append() (doesn't matter what that is, it'll get overwritten).
  2. To insert an element, existing elements must be shifted (copied over to 1 index higher) to make room for that element, e.g. using the builtin copy() (elements you want to insert before).
  3. Set the element at the proper index, using a single assignment.

In code:

array1 := []int{1, 3, 4, 5}
array2 := []int{2, 4, 6, 8}

array1 = append(array1, 0)   // Step 1
copy(array1[2:], array1[1:]) // Step 2
array1[1] = array2[2]        // Step 3

fmt.Println(array1)

Output (try it on the Go Playground):

[1 6 3 4 5]

Optimization in special cases

Note that in some special cases (when the slice element is big, like a big struct), it may be faster to append the last element, and then it's enough to copy 1 less elements (because the appended last element is right where it needs to be).

This is how it looks like:

last := len(array1) - 1
array1 = append(array1, array1[last]) // Step 1
copy(array1[2:], array1[1:last])      // Step 2
array1[1] = array2[2]                 // Step 3

This will result in the same slice. Try this one on the Go Playground.

icza
  • 389,944
  • 63
  • 907
  • 827
  • 1
    Most may know this, make sure to remember slices are just special references to arrays and in some cases, like if `array1` is a slice of a slice and the original slice is hanging around somewhere, the append overwrites the next element in the array no matter who else may be using it it. It may cause you a headache [Go Playground](https://play.golang.org/p/yelagAXPdKf). – stoksc Feb 13 '21 at 16:10
  • 10
    Generous use of the word Simple – Robert Mar 02 '21 at 18:51
  • 2
    Note, the Optimization version will panic if `len(array1) == 0`. It will require additional code to test for that. – Venning Jun 24 '22 at 23:48
12

I found the question setup pretty tricky to follow.

Rephrased, they want to insert an element. Here we have an array where it's missing the element 3 and we want to insert it.

package main

import (
    "fmt"
)

func main() {
    a := []int{1, 2, 4, 5, 6}
    b := 3

    // Make space in the array for a new element. You can assign it any value.
    a = append(a, 0)   
    fmt.Println(a)

    // Copy over elements sourced from index 2, into elements starting at index 3.
    copy(a[3:], a[2:])  
    fmt.Println(a)

    a[2] = b         
    fmt.Println(a)
}
Don P
  • 60,113
  • 114
  • 300
  • 432
3

extending the answer from @Volker, i put the answer here https://play.golang.org/p/3Hla2y2ava too if you want to test it.

package main

import "fmt"

func main() {
    array1 := []int{1, 3, 4, 5}
    array2 := []int{2, 4, 6, 8}
    temp := append([]int{array2[2]}, array1[1:]...)
    array1 = append(array1[:1], temp...)
    fmt.Println(array1)
}
ngurajeka
  • 66
  • 3
2

The following solution worked for me

func insert(a []int, c int, i int) []int {
    return append(a[:i], append([]int{c}, a[i:]...)...)
}

You can make it more general via empty interfaces

func insert(a []interface{}, c interface{}, i int) []interface{} {
    return append(a[:i], append([]interface{}{c}, a[i:]...)...)
}
Yaroslav Bai
  • 1,056
  • 11
  • 14
  • plus'd to support answerer, however, i do not endorse to recommend that solution. imho, the OP question is hardly factorized in a function that satisfies as many requirements as the std append builtin function. –  Nov 25 '19 at 16:53
2

Most of the answers are old and there is a official slices.Insert function.

Docs

Its header is:

func Insert[S ~[]E, E any](s S, i int, v ...E) S

Your use case:

package main

import (
    "fmt"
    "slices"
)

func main() {
    array1 := []int{1, 3, 4, 5}
    array2 := []int{2, 4, 6, 8}

    array1 = slices.Insert(array1, 1, array2[2])
    fmt.Println(array1) // prints: [1 6 3 4 5]
}

Note that it returns the new slice, not modifies the input parameter in-place.

You can see how it is implemented here. It also takes cap of slice into considiration and makes appropriate move if the new len exceeds the current cap.

ufukty
  • 312
  • 3
  • 10
1

Based on icza's post i wrote a function to shift the slice / array which I want to share with you:

package main

import "fmt"

func main() {
    s := []string{"a", "c", "d"}
    shiftArray(&s, 1, "b")
    fmt.Println(s)

}

func shiftArray(array *[]string, position int, value string) {
    //  extend array by one
    *array = append(*array, "")

    // shift values
    copy((*array)[position+1:], (*array)[position:])

    // insert value
    (*array)[position] = value
}
fty4
  • 568
  • 9
  • 18
1

Here a one-liner:

append(array1[:1], append(array2[2:3], array1[1:]...)...)

Go Playground

suren
  • 7,817
  • 1
  • 30
  • 51
0

I don't know is it optimal or not, but this piece of code works for me:

func sliceins(arr []int, pos int, elem int) []int { //insert element before pos in slice. if pos >= len(arr) insert into tail
    if pos < 0 {
        pos = 0
    } else if pos >= len(arr) {
        pos = len(arr)
    }
    out := make([]int, len(arr)+1)
    copy(out[:pos], arr[:pos])
    out[pos] = elem
    copy(out[pos+1:], arr[pos:])
    return out
}

In Your case just call

sliceins(array1, 1, array2[2])
NoAngel
  • 1,072
  • 2
  • 18
  • 27
  • code above can be easily changed to support another type or 2D array – NoAngel Jun 28 '18 at 09:05
  • 2
    Looks good, but I'd probably avoid that if statement at the top and let it panic if you pass in invalid indexes, to conform to how regular slices behave. – John Gibb Jun 28 '18 at 12:35
0

I answered a similar question in other thread. Anyway I used the following methods to play with slices and index:

func insertInt(array []int, value int, index int) []int {
    return append(array[:index], append([]int{value}, array[index:]...)...)
}

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

func moveInt(array []int, srcIndex int, dstIndex int) []int {
    value := array[srcIndex]
    return insertInt(removeInt(array, srcIndex), value, dstIndex)
}

You can play with it here:

https://play.golang.org/p/Sfu1VsySieS

I hope it'll help you

omotto
  • 1,721
  • 19
  • 20
0

how about:

append(append(slice[:i], elem), slice[i:]...)
seebi
  • 488
  • 5
  • 9
0

I found it's hard to wrap my head around a = append(a[:index+1], a[index:]...) Also as @lieblos warned, working on the original slice is tricky.

Here is an easier solution, though not memory efficient:

  var array3 []int
  array3 = append(array3, array1[:1]...)
  array3 = append(array3, array2[2])
  array3 = append(array3, array1[1:]...)
  array1 = array3

https://go.dev/play/p/rni5hgB0JNt

B.Z.
  • 418
  • 5
  • 12