Insert a value in a slice at a given index
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?
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 int
s:
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:]...)
- This makes sure the array has enough capacity to accommodate the new element.
- This copies all required elements to one index higher to make room for the new element.
- Set the element at the index, using a single assignment:
a[index] = value
Which is more efficient, according to the benchmarks.
Simple, efficient and logical way:
- Make sure
array1
has enough capacity (length) to accomodate the new, insertable element. To do that, append a single element using the builtingappend()
(doesn't matter what that is, it'll get overwritten). - 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). - 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.
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)
}
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)
}
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:]...)...)
}