Slice order guarantee based on order of insertion - go

This is super trivial question but I could not find it asked here. Sorry if I missed it.
Can I count that the order in slice is always the order of insertion of elements in that slice?
I tested it with the following code:
func main() {
for i := 0; i < 10000; i++ {
testOrder()
}
}
func testOrder() {
sl := []int{}
for i := 0; i < 50; i++ {
sl = append(sl, i)
}
for i, el := range sl {
if el != i {
panic("Order not quaranteed")
}
}
}
Fill a slice with numbers and then check if the order of elements in the slice is as they were populated in it. I do this check 10000 times.

You're not inserting but appending. append() always appends the elements to the end of the slice, and it does not leave "holes". So yes, you can count on that your example will never panic with "Order not guaranteed".
Read more about the append() function in its doc: https://golang.org/pkg/builtin/#append
Also in the Spec: Appending to and copying slices
Also read official blog post: The Go Blog: Arrays, slices (and strings): The mechanics of 'append'

Slices and arrays are sequentially ordered structures. The order of elements will never change if you append to them, even if they are copied in memory.

Related

Reading from a slice of unknown length in Golang

I'm trying to replicate this algorithm for finding duplicates in an array in Golang. Here's the javascript version:
function hasDuplicateValue(array) {
let existingNumbers = [];
for(let i = 0; i < array.length; i++) {
if(existingNumbers[array[i]] === 1) {
return true;
} else {
existingNumbers[array[i]] = 1;
}
}
return false;
}
On line 2, the algorithm creates an empty array of unknown length, and then adds 1 to an index in the array corresponding with each number that it finds (e.g. if it finds the number 3 in the array, it will add a 1 to index 3 in existing numbers.
I'm wondering — how do I replicate this in Golang (since we need to have slots allocated in the slice before reading it). Would I first need to find the max value in the array and then declare the existingNumbers slice to be of that same size?
Or is there a more efficient way of doing this (instead of searching through the array and finding the max value before constructing the slice).
Thanks!
Edit:
I realized that I can't do this with a slice because I can't read from an empty value. However, as #icza suggested, it will work with a map:
func findDuplicates(list []int)(bool) {
temp := make(map[int]int)
for _, elem := range list {
if temp[elem] == 1 {
return true
} else {
temp[elem] = 1
}
}
return false
}
As comments, I would also suggest using a map to keep the state of the duplications, but we can use map[int]struct{} because empty structs are not consumed any memory in Go.
And also I have simplified the code a bit and it is as follows.
func findDuplicates(list []int) bool {
temp := make(map[int]struct{})
for _, elem := range list {
if _, ok := temp[elem]; ok {
return true
}
temp[elem] = struct{}{}
}
return false
}
Full code can be executed here

Sorting a slice based on the order of the elements in another slice

I am attempting to order a slice based on the order of the elements within another slice. My sort function works when I only have one of each type within my slice I want to order however when I start adding more elements the ordering breaks.
I have created an example within the Golang playground.
https://play.golang.org/p/e9sHIeV2qSf
I want to order my Variant slice by the Code field and have it the same as order as the codes appear in the Language struct.
Below is the sort function I am using:
sort.Slice(variants, func(i, j int) bool {
for k, language := range languages {
if language.Code == variants[i].Code {
return i >= k
}
}
return false
})
The current order it's returning is:
Sorted slice: [{Code:en-GB} {Code:en-US} {Code:en-GB} {Code:es-ES}
{Code:en-GB} {Code:en-GB} {Code:en-GB} {Code:en-GB} {Code:es-ES}]
When the order within my Language struct is:
"en-GB", "en-US", "fr-FR", "es-ES"
I think to do this, you need to build a ranking of your languages:
var langMap map[string]int
for i, lang := range languages {
langMap[lang.Code] = i
}
With this, it becomes trivial to just look up the ranking of each item in variants, and return the appropriate value:
sort.Slice(variants, func(i, j int) bool {
iRank, jRank := langMap[variants[i].Code], langMap[variants[j].Code]
return iRank < jRank
})
If there's a chance you may have inputs that are not in the pre-sorted list, you can sort them last:
sort.Slice(variants, func(i, j int) bool {
iRank, iExists := langMap[variants[i].Code]
jRank, jExists := langMap[variants[j].Code]
switch (
case iExists && jExists:
// Both exist in the pre-ordered list, so sort by rank
return iRank < jRank
case !iExists && !jExists:
// Neither exists in the pre-ordered list, sort alphabetically
return variants[i].Code < variants[j].Code
case iExists:
// Only i exists, so sort it before j
return true
default: // jExists
// Only j exists, so sort it after i
return false
)
})
It is logically possible to do the same by looping through your reference list each time, as you're attempting, but it's much harder to reason about, and far less efficient.

When appending value to slice, value is different from original value

consider this piece of code:
package main
import (
"fmt"
)
func main() {
fmt.Println(Part(11))
}
func Part(n int) string {
enumResult := [][]int{}
enum(n, n, []int{}, &enumResult)
fmt.Println(enumResult)
fmt.Println(40, enumResult[40])
return ""
}
var abc int = 0
func enum(n int, top int, pre []int, result *[][]int) {
var i int
if n > top {
i = top
} else {
i = n
}
for ; i > 0; i-- {
tempResult := append(pre, i)
if n-i == 0 {
/* if tempResult[0] == 3 && tempResult[1] == 3 && tempResult[2] == 3 && tempResult[3] == 2 {
tempResult = append(tempResult, 12345)
}*/
fmt.Println(abc, tempResult)
abc++
*result = append(*result, tempResult)
} else {
enum(n-i, i, tempResult, result)
}
}
}
When I run this code
I append value '[3,3,3,2]' to 'enumResult'
but If I check the value of 'enumResult' then '[3,3,3,1]' is appear
it`s index is 40 =>enumResult[40]
(other value is correct)
I don`t know why this is happening
Can you explain to me why?
The problem is indeed due to append.
There are two thing about append. First is, that append doe not necessarily copy memory. As the spec specifies:
If the capacity of s is not large enough to fit the additional values,
append allocates a new, sufficiently large underlying array that fits
both the existing slice elements and the additional values. Otherwise,
append re-uses the underlying array.
This may cause unexpected behavior if you are not clear. A playground example: https://play.golang.org/p/7A3JR-5IX8o
The second part is, that when append does copy memory, it grows the capacity of the slice. However, it does not grow it just by 1. A playground example: https://play.golang.org/p/STr9jMqORUz
How much append grows a slice is undocumented and considered an implentation details. But till Go 1.10, it follows this rule:
Go slices grow by doubling until size 1024, after which they grow by
25% each time.
Note that when enabling race-detector, this may change. The code for growing slice is located in $GOROOT/src/runtime/slice.go in growslice function.
Now back to the question. It should be clear now that your code did append from a same slice with sufficient capacity due to growth of the slice from append before. To solve it, make a new slice and copy the memory.
tempResult := make([]int,len(pre)+1)
copy(tempResult,pre)
tempResult[len(pre)] = i

Remove slice element within a for

An idiomatic method to remove an element i from a slice a, preserving the order, seems to be:
a = append(a[:i], a[i+1:]...)
I was wondering which would be the best way to do it inside a loop. As I understand, it is not possible to use it inside a range for:
for i := range a { // BAD
if conditionMeets(a[i]) {
a = append(a[:i], a[i+1:]...)
}
}
However it is possible to use len(a). [EDIT: this doesn't work, see answers below]
for i := 0; i < len(a); i++ {
if conditionMeets(a[i]) {
a = append(a[:i], a[i+1:]...)
}
}
Is there a better or more idiomatic way than using len or append?
Your proposed solution is incorrect. The problem is that when you remove an element from a slice, all subsequent elements are shifted. But the loop doesn't know that you changed the underlying slice and loop variable (the index) gets incremented as usual, even though in this case it shouldn't because then you skip an element.
And if the slice contains 2 elements which are right next to each other both of which need to be removed, the second one will not be checked and will not be removed.
So if you remove an element, the loop variable has to be decremented manually! Let's see an example: remove words that start with "a":
func conditionMeets(s string) bool {
return strings.HasPrefix(s, "a")
}
Solution (try it with all other examples below on the Go Playground):
a := []string{"abc", "bbc", "aaa", "aoi", "ccc"}
for i := 0; i < len(a); i++ {
if conditionMeets(a[i]) {
a = append(a[:i], a[i+1:]...)
i--
}
}
fmt.Println(a)
Output:
[bbc ccc]
Or better: use a downward loop and so you don't need to manually decrement the variable, because in this case the shifted elements are in the "already processed" part of the slice.
a := []string{"abc", "bbc", "aaa", "aoi", "ccc"}
for i := len(a) - 1; i >= 0; i-- {
if conditionMeets(a[i]) {
a = append(a[:i], a[i+1:]...)
}
}
fmt.Println(a)
Output is the same.
Alternate for many removals
If you have to remove "many" elements, this might be slow as you have to do a lot of copy (append() does the copy). Imagine this: you have a slice with 1000 elements; just removing the first element requires copying 999 elements to the front. Also many new slice descriptors will be created: every element removal creates 2 new slice descriptors (a[:i], a[i+1:]) plus a has to be updated (the result of append()). In this case it might be more efficient to copy the non-removable elements to a new slice.
An efficient solution:
a := []string{"abc", "bbc", "aaa", "aoi", "ccc"}
b := make([]string, len(a))
copied := 0
for _, s := range(a) {
if !conditionMeets(s) {
b[copied] = s
copied++
}
}
b = b[:copied]
fmt.Println(b)
This solution allocates a slice with the same length as the source, so no new allocations (and copying) will be performed. This solution can also use the range loop. And if you want the result in a, assign the result to a: a = b[:copied].
Output is the same.
In-place alternate for many removals (and for general purposes)
We can also do the removal "in place" with a cycle, by maintaining 2 indices and assigning (copying forward) non-removable elements in the same slice.
One thing to keep in mind is that we should zero places of removed elements in order to remove references of unreachable values so the GC can do its work. This applies to other solutions as well, but only mentioned here.
Example implementation:
a := []string{"abc", "bbc", "aaa", "aoi", "ccc"}
copied := 0
for i := 0; i < len(a); i++ {
if !conditionMeets(a[i]) {
a[copied] = a[i]
copied++
}
}
for i := copied; i < len(a); i++ {
a[i] = "" // Zero places of removed elements (allow gc to do its job)
}
a = a[:copied]
fmt.Println(a)
Output is the same. Try all the examples on the Go Playground.

Standard library Priority Queue push method

The code snippet below is the library implementation of the push methods for a priority queue. I am wondering why the line with the code a = a[0 : n+1] does not throw an out of bounds errors.
func (pq *PriorityQueue) Push(x interface{}) {
// Push and Pop use pointer receivers because they modify the slice's length,
// not just its contents.
// To simplify indexing expressions in these methods, we save a copy of the
// slice object. We could instead write (*pq)[i].
a := *pq
n := len(a)
a = a[0 : n+1]
item := x.(*Item)
item.index = n
a[n] = item
*pq = a
}
a slice is not an array; it is a view onto an existing array. The slice in question is backed by an array larger than itself. When you define a slice of an existing slice, you're actually slicing the underlying array, but the indexes referenced are relative to the source slice.
That's a mouthful. Let's prove this in the following way: we'll create a slice of zero length, but we'll force the underlying array to be larger. When creating a slice with make, the third parameter will set the size of the underlying array. The expression make([]int, 0, 2) will allocate an array of size 2, but it evaluates to a size-zero slice.
package main
import ("fmt")
func main() {
// create a zero-width slice over an initial array of size 2
a := make([]int, 0, 2)
fmt.Println(a)
// expand the slice. Since we're not beyond the size of the initial
// array, this isn't out of bounds.
a = a[0:len(a)+1]
a[0] = 1
fmt.Println(a)
fmt.Println(a[0:len(a)+1])
}
see here. You can use the cap keyword to reference the size of the array that backs a given slice.
The specific code that you asked about loops over cap(pq) in the calling context (container/heap/example_test.go line 90). If you modify the code at the call site and attempt to push another item into the queue, it will panic like you expect. I ... probably wouldn't suggest writing code like this. Although the code in the standard library executes, I would be very sour if I found that in my codebase. It's generally safer to use the append keyword.
Because it works in a specific example program. Here are the important parts from the original/full example source)
const nItem = 10
and
pq := make(PriorityQueue, 0, nItem)
and
for i := 0; i < cap(pq); i++ {
item := &Item{
value: values[i],
priority: priorities[i],
}
heap.Push(&pq, item)
}
Is it an example from container/heap? If yes, then it doesn't throws an exception because capacity is big enough (see how the Push method is used). If you change the example to Push more items then the capacity, then it'll throw.
It does in general; it doesn't in the container/heap example. Here's the general fix I already gave you some time ago.
func (pq *PriorityQueue) Push(x interface{}) {
a := *pq
n := len(a)
item := x.(*Item)
item.index = n
a = append(a, item)
*pq = a
}
Golang solution to Project Euler problem #81

Resources