I want to clone a [][]float64 slice
in Go
without affecting the original array. How can I do it?
In the below example, I want the slice a1
to remain unaffected when a2
is changed. At present, I am using the built in append function of Go. But I have not been able to get the desired functionality.
package main
import (
"fmt"
)
func main() {
a1 := [][]float64{[]float64{1.0,1.0}, []float64{2.0,2.1}}
a2 := make([][]float64, len(a1))
a2 = append([][]float64{}, a1...)
fmt.Println(a2, a1) // At this step, a1 has not changed.
a2[1][0] = 5.0 //Change value of one element of a2.
fmt.Println(a2, a1) // At this step, a1 has changed.
}
>> [[1 1] [2 2.1]] [[1 1] [2 2.1]]
>> [[1 1] [5 2.1]] [[1 1] [5 2.1]]
When I use copy function of Go, I find that it supports int
datatype. I get the following error when I use the copy function. Understandably, the below error is because of Type mismatch between what copy expects in Go.
cannot use copy(a2, a1) (type int) as type [][]float64 in assignment
I want to use slices and not arrays.
I am using this reference. I am new to Go and will appreciate any help.
To duplicate a slice in Go, getting a deep copy of its contents, you need to either use the built-in copy() function, or create a new empty slice and add all the elements of the first slice to it using the append() function.
But arrays in Golang cannot be resized, hence we have slices, whose size can be dynamically changed. Slices have length and capacity. Length is the number of elements present in the slice.
Setting the slice to nil is the best way to clear a slice. nil slices in go are perfectly well behaved and setting the slice to nil will release the underlying memory to the garbage collector.
Slices in Go and Golang The basic difference between a slice and an array is that a slice is a reference to a contiguous segment of an array. Unlike an array, which is a value-type, slice is a reference type. A slice can be a complete array or a part of an array, indicated by the start and end index.
A multi-dimensional slice is a slice of slices. You have to clone each slice individually, in case of a 2D slice using a loop.
Something like this:
package main
import (
"fmt"
)
func Clone(arr [][]float64) (res [][]float64) {
res = make([][]float64, len(arr))
for i := range arr {
res[i] = append([]float64{}, arr[i]...)
}
return
}
func main() {
a1 := [][]float64{{1.0, 1.0}, {2.0, 2.1}}
a2 := Clone(a1)
fmt.Println(a2, a1) // At this step, a1 has not changed.
a2[1][0] = 5.0 //Change value of one element of a2.
fmt.Println(a2, a1)
}
Prints
[[1 1] [2 2.1]] [[1 1] [2 2.1]]
[[1 1] [5 2.1]] [[1 1] [2 2.1]]
You can use the range
method to iterate over your 2D slice,inside the loop create a temporary slice of the same length as your inner slice and assign it to destination slice index,after that you can use the copy
method.Here is the code with same logic:
package main
import (
"fmt"
)
func main() {
a1 := [][]float64{[]float64{1.0, 1.0}, []float64{2.0, 2.1}}
a3 := make([][]float64, len(a1))
copySlice(a3, a1)
fmt.Println(a3, a1)
a3[1][0] = 10.0
fmt.Println(a3, a1)
}
func copySlice(dest [][]float64, src [][]float64) {
for i := range src {
tmp := make([]float64, len(src[i]))
dest[i] = tmp
copy(dest[i], src[i])
}
return
}
Output:
[[1 1] [2 2.1]] [[1 1] [2 2.1]]
[[1 1] [10 2.1]] [[1 1] [2 2.1]]
The provided answers are correct, however they allocate memory in a loop, and memory allocation is expensive.
There exists a more optimized way. In general you can create and fill a 2D slice with exactly 2 calls to make
: 1 small to hold slice headers, and 1 large to hold all the values contiguously. The trick is that each result slice is a ... slice of the large buffer.
func AllocAndCopy(arr [][]float64) (res [][]float64) {
// 1 alloc of slice of slices, for the result
res = make([][]float64, len(arr))
// 1 alloc of a large slice, for the contents
size := 0
for _, x := range arr {
size += len(x)
}
mem := make([]float64, size)
// No more alloc during the copying
for i, x := range arr {
res[i] = mem[:len(x):len(x)]
mem = mem[len(x):]
copy(res[i], x)
}
return
}
In this benchmark with very small sample values, AllocAndCopy
is ~2x as fast as Clone
and copySlice
.
It is possible to write AllocAndCopy
with fewer lines of code if all the slices have the exact same length, instead of an arbitrary length for each.
Copying the elements of slice works, but copying a slice does not because a slice does not contain the elements itself. It acts as references to an underlying array which is not duplicated when manipulating slices. Even using the copy
command on a slice won't result in a duplicate of the underlying array.
This is described in A Tour of Go: https://tour.golang.org/moretypes/8
Slices are like references to arrays
A slice does not store any data, it just describes a section of an underlying array.
Changing the elements of a slice modifies the corresponding elements of its underlying array.
Other slices that share the same underlying array will see those changes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With