Insert a value in a slice at a given index

GoSlice

Go Problem Overview


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?

Go Solutions


Solution 1 - Go

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.

Solution 2 - Go

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.

Solution 3 - Go

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)
}

Solution 4 - Go

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)
}

Solution 5 - Go

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:]...)...)
}

Solution 6 - Go

Here a one-liner:

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

Go Playground

Solution 7 - Go

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
}

Solution 8 - Go

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

Solution 9 - Go

how about:

append(append(slice[:i], elem), slice[i:]...)

Solution 10 - Go

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

Solution 11 - Go

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])

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionchefcurry7View Question on Stackoverflow
Solution 1 - GowasmupView Answer on Stackoverflow
Solution 2 - GoiczaView Answer on Stackoverflow
Solution 3 - GoDon PView Answer on Stackoverflow
Solution 4 - GongurajekaView Answer on Stackoverflow
Solution 5 - GoYaroslav BaiView Answer on Stackoverflow
Solution 6 - GosurenView Answer on Stackoverflow
Solution 7 - Gofty4View Answer on Stackoverflow
Solution 8 - GoomottoView Answer on Stackoverflow
Solution 9 - GoseebiView Answer on Stackoverflow
Solution 10 - GoB.Z.View Answer on Stackoverflow
Solution 11 - GoNoAngelView Answer on Stackoverflow