1

I am trying to learn Go, so here is my very simple function for removing adjacent duplicates from slice for exercise from the book by Donovan & Kernighan.
Here is the code: https://play.golang.org/p/avHc1ixfck

package main
import "fmt"

func main() {
    a := []int{0, 1, 1, 3, 3, 3}
    removeDup(a)
    fmt.Println(a)
}

func removeDup(s []int) {
    n := len(s)
    tmp := make([]int, 0, n)
    tmp = append(tmp, s[0])
    j := 1
    for i := 1; i < n; i++ {
        if s[i] != s[i-1] {
            tmp = append(tmp, s[i])
            j++
        }
    }
    s = s[:len(tmp)]
    copy(s, tmp)
}

It should print out [0 1 3] - and I checked, actually tmp at the end of the function it has desired form. However, the result is [0 1 3 3 3 3]. I guess there is something with copy function.

Can I somehow replace input slice s with the temp or trim it to desired length?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Photon Light
  • 757
  • 14
  • 26

2 Answers2

4

Option 1

Return a new slice as suggested by @zerkms.
https://play.golang.org/p/uGJiD3WApS

package main
import "fmt"

func main() {
    a := []int{0, 1, 1, 3, 3, 3}
    a = removeDup(a)
    fmt.Println(a)
}

func removeDup(s []int) []int {
    n := len(s)
    tmp := make([]int, 0, n)
    tmp = append(tmp, s[0])
    for i := 1; i < n; i++ {
        if s[i] != s[i-1] {
            tmp = append(tmp, s[i])
        }
    }
    return tmp
}

Option 2
Use pointers for pass-by-reference.
The same thing in effect as that of option1.

https://play.golang.org/p/80bE5Qkuuj

package main

import "fmt"

func main() {
    a := []int{0, 1, 1, 3, 3, 3}
    removeDup(&a)
    fmt.Println(a)
}

func removeDup(sp *[]int) {
    s := *sp
    n := len(s)
    tmp := make([]int, 0, n)
    tmp = append(tmp, s[0])
    for i := 1; i < n; i++ {
        if s[i] != s[i-1] {
            tmp = append(tmp, s[i])
        }
    }
    *sp = tmp
}

Also, refer to following SO thread: Does Go have no real way to shrink a slice? Is that an issue?

hbagdi
  • 485
  • 6
  • 18
  • I knew that returning new slice and assigning it to previously declared slice would do the work, but I wanted to do this in function. Pointers seems to be the desired solution, thank you! I've got Python background, still can't get used to using pointers. – Photon Light Jul 23 '17 at 21:42
  • You'll get them eventually and feel really dumb about yourself when you realize it's an awe(some/ful) concept in programming. That's what I felt! :) – hbagdi Jul 23 '17 at 22:41
0

Here's two more slightly different ways to achieve what you want using sets and named types. The cool thing about named types is that you can create interfaces around them and can help with the readability of lots of code.

package main

import "fmt"

func main() {
    // returning a list
    a := []int{0, 1, 1, 3, 3, 3}
    clean := removeDup(a)
    fmt.Println(clean)
    // creating and using a named type
    nA := &newArrType{0, 1, 1, 3, 3, 3}
    nA.removeDup2()
    fmt.Println(nA)

    // or... casting your orginal array to the named type
    nB := newArrType(a)
    nB.removeDup2()
    fmt.Println(nB)
}

// using a set
// order is not kept, but a set is returned
func removeDup(s []int) (newArr []int) {
    set := make(map[int]struct{})
    for _, n := range s {
        set[n] = struct{}{}
    }
    newArr = make([]int, 0, len(set))
    for k := range set {
        newArr = append(newArr, k)
    }
    return
}

// using named a typed
type newArrType []int

func (a *newArrType) removeDup2() {
    x := *a
    for i := range x {
        f := i + 1
        if f < len(x) {
            if x[i] == x[f] {
                x = x[:f+copy(x[f:], x[f+1:])]
            }
        }
    }
    // check the last 2 indexes
    if x[len(x)-2] == x[len(x)-1] {
        x = x[:len(x)-1+copy(x[len(x)-1:], x[len(x)-1+1:])]
    }
    *a = x
}
reticentroot
  • 3,612
  • 2
  • 22
  • 39
  • Just for the sake of curiosity, I want some feedback on the following thought- While this is a perfectly acceptable solution to me as it makes the OOPSy, isn't it too much for such a case? Would it make sense to not have 'removeDup' method associated with a type, but rather keep it in a separate?? – hbagdi Jul 23 '17 at 21:52
  • It might be overkill for such a small example, but what if this was a huge project, or what if you wanted to used this concurrently, or removeDups needed to operation on something more generic like an interface (we hope not), or removeDups is only one operation on a chain of operations that operate on a pointer. On the larger project separation of types is easier to manage. Concurrency can be handled by extending then named type was locks and semaphores. Chained pointer operations are easier to write and maybe read. See here as well https://hunterloftis.github.io/2017/07/12/renamed-types/ – reticentroot Jul 23 '17 at 21:58
  • Right, that makes sense, thanks for the detailed reply. – hbagdi Jul 23 '17 at 22:04
  • Thank you, looks like a nice answer, I just need some more go knowledge to understand ;) – Photon Light Jul 24 '17 at 06:36