Remove Adjacent Duplicates in string slice - arrays

I have a problem statement to write an in-place function to eliminate the adjacent duplicates in a string slice.
I came up with the following code
func main() {
tempData := []string{"abc", "abc", "abc", "def", "def", "ghi"}
removeAdjacentDuplicates(tempData)
fmt.Println(tempData)
}
func removeAdjacentDuplicates(data []string) {
for j := 1; j < len(data); {
if data[j-1] == data[j] {
data = append(data[:j], data[j+1:]...)
} else {
j++
}
}
fmt.Println(data)
}
The output is following
[abc def ghi]
[abc def ghi ghi ghi ghi]
My doubt is, if in the function, slice is modified, then in the calling function, why isn't the slice giving correct results?
Also, any article to understand the slices (and the underlying array) much better would be very helpful.

The func removeAdjacentDuplicate takes the slice "as if" it is a reference to tempData
The capacity and length of tempData in the main() stays the same for the lifetime
of the program
In the removeAdjacentDuplicate func each time a dupe is found the final value of "ghi" is moved from the end to the end - 1. So in the memory at the end of the
slice there are repeated "ghi"
When the control returns to the main, the program prints out the now modified
slice tempData. Because it was passed in a similar way to a reference to the
function it is this memory that was modified. The function call did not make a copy of the memory
You can see this behaviour by looking at the cap() and len() as the program runs
package main
import (
"fmt"
)
func main() {
tempData := []string{"abc", "abc", "abc", "def", "def", "ghi"}
removeAdjacentDuplicates(tempData)
fmt.Println(tempData,cap(tempData),len(tempData))
}
func removeAdjacentDuplicates(data []string) {
for j := 1; j < len(data); {
if data[j-1] == data[j] {
data = append(data[:j], data[j+1:]...)
fmt.Println(data,cap(data),len(data))
} else {
j++
}
}
fmt.Println(data, cap(data),len(data))
}

In your code, removeAdjacentDuplicates wants to mutate the slcie passed in argument. This is not really possible.
This function should return the new slice, just like append does.
func removeAdjacentDuplicates(data []string) []string{
for j := 1; j < len(data); {
if data[j-1] == data[j] {
data = append(data[:j], data[j+1:]...)
} else {
j++
}
}
return data
}
If you really want to mutate the argument, it is possible but you need to pass a pointer to a slice *[]string

Go 1.18 and above
You can use slices.Compact exactly for this.
Compact replaces consecutive runs of equal elements with a single copy. This is like the uniq command found on Unix. Compact modifies the contents of the slice s; it does not create a new slice.
func main() {
data := []string{"abc", "abc", "abc", "def", "def", "ghi"}
data = slices.Compact(data)
fmt.Println(data) // [abc def ghi]
}
The package is golang.org/x/exp/slices, which is still experimental. If you don't want to import an exp package, you can copy the source:
func Compact[S ~[]E, E comparable](s S) S {
if len(s) == 0 {
return s
}
i := 1
last := s[0]
for _, v := range s[1:] {
if v != last {
s[i] = v
i++
last = v
}
}
return s[:i]
}
If the slice's elements aren't comparable, use slices.CompactFunc which works the same but takes also a comparator function.

Try this function:
func deleteAdjacentDuplicate(slice []string) []string {
for i := 1; i < len(slice); i++ {
if slice[i-1] == slice[i] {
copy(slice[i:], slice[i+1:]) //copy [4] where there is [3, 4] => [4, 4]
slice = slice[:len(slice)-1] //removes last element
i-- //avoid advancing counter
}
}
return slice
}

Related

How to store struct with map into an Array in the go language

I made a package that looks like this...
package foo
type Foo struct {
num int
aMap map[int](int)
}
func MakeFoo() BookState {
return Foo{
num: -1,
aMap: make(map[int](int)),
}
}
I'm processing rows of a file like this
nrows :=100
arrayFoo = make([]Foo, nrows)
Foo = foo.MakeFoo()
count := 0
for int i=0; i < nrows; i++ {
row = myWrappedReader.ReadLine()
foo.num = i
foo.aMap[key] += row.otherNum
arrayFoo[i] = foo
}
But then when I go to check the arrayFoo at the end I have something that looks like this
[{num:1, aMap:{/*final state*/}, {num:2, aMap:{/*final state*/}, ...]
So the integer is updating but I need a copy of aMap to be stored instead of just the pointer to aMap.
Update:
Here's a playground.
Update2:
Here's a version that works. My class is quite a bit more complicated than this so I think I'll write a helper function in package foo that clone it.
Is there a easier way to copy maps or do most people do that?
Anything requiring a deep copy, as mentioned in "Is there a built in function in go for making copies of arbitrary maps?", would involve a dedicated function.
Example in this gist:
package deepcopy
import (
"bytes"
"encoding/gob"
)
func init() {
gob.Register(map[string]interface{}{})
}
// Map performs a deep copy of the given map m.
func Map(m map[string]interface{}) (map[string]interface{}, error) {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
dec := gob.NewDecoder(&buf)
err := enc.Encode(m)
if err != nil {
return nil, err
}
var copy map[string]interface{}
err = dec.Decode(&copy)
if err != nil {
return nil, err
}
return copy, nil
}
Based on the suggestion of mkopriva replacing the line foo.aMap[key] += i with foo.aMap = map[string]int{"key": foo.aMap[key] + i}
package main
import (
"fmt"
)
type Foo struct {
num int
aMap map[string](int)
}
func MakeFoo() Foo {
return Foo{
num: -1,
aMap: make(map[string](int)),
}
}
func main() {
foo := MakeFoo()
key := "tmp"
foo.aMap[key] = 0
fmt.Println(foo)
arrayFoo := make([]Foo, 10)
for i := 0; i < 10; i++ {
foo.num = i
foo.aMap = map[string]int{"key": foo.aMap[key] + i}
arrayFoo[i] = foo
}
fmt.Println(arrayFoo)
}
Output:
{-1 map[tmp:0]}
[{0 map[key:0]} {1 map[key:1]} {2 map[key:2]} {3 map[key:3]} {4 map[key:4]} {5 map[key:5]} {6 map[key:6]} {7 map[key:7]} {8 map[key:8]} {9 map[key:9]}]

Repeat array item in Go

I have an array of array of strings.
I want to repeat the "x" x times.
I do not want it to be in one string, like what happens using strings.Repeat(). I need it to be individual array items. Is that possible?
[]string{"x", "x", "x"}
You could do:
count := 10
strings.Split(strings.Repeat("x", count), "")
https://play.golang.org/p/zi0RqNT9lm9
or simply
func sliceFilledWithString(size int, str string) []string {
data := make([]string, size)
for i := 0; i < size; i++ {
data[i] = str
}
return data
}
https://play.golang.org/p/TR99bdH8ewX
With generics you can write a more general function. (Maybe something like this will be added to std lib.)
func sliceRepeat[T any](size int, v T) []T {
retval := make([]T, 0, size)
for i := 0; i < size; i++ {
retval = append(retval, v)
}
return retval
}
https://go2goplay.golang.org/p/5TIJRFNQPUY

Pass a slice of 2d int array without knowing the length of columns

In the function takeinput if i remove the 3 i get an error. My question what to do if i want to use this function to take in input for a different array whose column length is different.
package main
import ("fmt")
func takeinput(in [][3]int){
for i:=0;i<3;i++{
for j:=0;j<3;j++ {
fmt.Scanf("%d",&in[i][j])
}
}
}
func main(){
var a[3][3]int
fmt.Println("Enter the value for matrix one")
takeinput(a[:])
for i:=0;i<3;i++{
for j:=0;j<3;j++{
fmt.Printf("%d\t",a[i][j])
}
fmt.Println()
}
fmt.Println()
}
The error i get if i remove the 3 in input function.
# command-line-arguments
./matrix.go:19:13: cannot use a[:] (type [][3]int) as type [][]int in argument to takeinput
Use slice of slice instead, try this:
package main
import (
"fmt"
)
func takeinput(a [][]int) {
for i := range a {
for j := range a[i] {
fmt.Scanf("%d", &a[i][j])
}
}
}
func main() {
a := make([][]int, 3)
for i := range a {
a[i] = make([]int, 3)
}
fmt.Println("Enter the value for matrix one")
takeinput(a)
for i := range a {
for j := range a[i] {
fmt.Printf("%d\t", a[i][j])
}
fmt.Println()
}
fmt.Println()
}

How do I find the longest strings in the array?

Actually I am able to get it done using two loops in Go Language, for example if I have array as:
["aa", "aab", "bcd", "a", "cdf", "bb"]
I need to return strings with maxLength. So output will be:
["aab", "bcd", "cdf"]
Here's what I am doing.
package main
import "fmt"
func allLongestStrings(inputArray []string) []string {
maxLength := len(inputArray[0])
outputArray := []string{}
for _, value := range inputArray {
if len(value) > maxLength {
maxLength = len(value)
}
}
for _, val := range inputArray {
if len(val) == maxLength {
outputArray = append(outputArray, val)
}
}
return outputArray
}
func main() {
xs := []string{"aa", "aab", "bcd", "a", "cdf", "bb"}
fmt.Println(allLongestStrings(xs))
}
Is it possible to do this in one loop because I am running the same loop twice to find length and to append strings in outputArray.
Thanks In Advance.
Try this:
func allLongestStrings(inputArray []string) []string {
max := -1 // -1 is guaranteed to be less than length of string
var result []string
for _, s := range inputArray {
if len(s) < max {
// Skip shorter string
continue
}
if len(s) > max {
// Found longer string. Update max and reset result.
max = len(s)
result = result[:0]
}
// Add to result
result = append(result, s)
}
return result
}
As peterSO points out in another answer, the result slice can have a capacity larger than required and can contain string values past the length of slice. The extra allocation and string references may be a problem in some contexts (result is retained for a long time, strings are large, ...). Return a copy of the slice if the allocation and references are a concern.
func allLongestStrings(inputArray []string) []string {
...
return append([]string(nil), result...)
}
If the function can mutate the original slice, then the function result can be constructed in the input slice. This avoids the allocation of the result slice.
func allLongestStrings(inputArray []string) []string {
n := 0
max := -1
for i, s := range inputArray {
if len(s) < max {
// Skip shorter string
continue
}
if len(s) > max {
// Found longer string. Update max and reset result.
max = len(s)
n = 0
}
inputArray[n], inputArray[i] = inputArray[i], inputArray[n]
n++
}
return inputArray[:n]
}
I would do it by using the sort package. Basically, what you do is to create a custom sort function by implementing sort.Interface and use sort.Sort to your advantage.
package main
import "sort"
import "fmt"
type sortByLength []string
// Len implements Len of sort.Interface
func (s sortByLength) Len() int {
return len(s)
}
// Swap implements Swap of sort.Interface
func (s sortByLength) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// Less implements Less of sort.Interface
func (s sortByLength) Less(i, j int) bool {
return len(s[i]) > len(s[j])
}
func main() {
toFind := []string{"aa", "aab", "bcd", "a", "cdf", "bb"}
// We sort it by length, descending
sort.Sort(sortByLength(toFind))
// The first element is sure to be the longest
longest := []string{toFind[0]}
// In case we have more than one element in toFind...
if len(toFind) > 1 {
// ...we need to find all remaining elements of toFind...
for _, str := range toFind[1:] {
// ...which are not smaller than the first element of longest.
if len(str) < len(longest[0]) {
// In case the current element is smaller in length, we can stop iterating
// over toFind.
break
}
// We know that str has the same length as longest[0], so we append it
longest = append(longest, str)
}
}
fmt.Println(longest)
}
Run it on Playground
However, while only having one loop in your own code, the sorting obviously iterates over the input, too.
For example, a more efficient version of #ThunderCat's solution,
package main
import "fmt"
func longest(a []string) []string {
var l []string
if len(a) > 0 {
l = append(l, a[0])
a = a[1:]
}
for _, s := range a {
if len(l[0]) <= len(s) {
if len(l[0]) < len(s) {
l = l[:0]
}
l = append(l, s)
}
}
return append([]string(nil), l...)
}
func main() {
a := []string{"aa", "aab", "bcd", "a", "cdf", "bb"}
fmt.Println(len(a), a)
l := longest(a)
fmt.Println(len(l), cap(l), l)
}
Playground: https://play.golang.org/p/JTvl4wVvSEK
Output:
6 [aa aab bcd a cdf bb]
3 4 [aab bcd cdf]
Reading #ThunderCat's solution, there is room for improvement. For example, for maximum and minimum problems, avoid using special values as an initial maximum or minimimun value. Don't overallocate memory and don't leave dangling pointers.
A Go string is implemented as:
type stringStruct struct {
str unsafe.Pointer
len int
}
If the list consists of 1,000 strings of length 1,000 followed by one string of length 1,001, the returned list will have a length of one and a capacity of at least 1,000. 999 entries have dangling pointers to 1,000 byte strings which the Go gc will be unable to release, wasting over one megabyte.
package main
import (
"fmt"
"strings"
"unsafe"
)
type stringStruct struct {
str unsafe.Pointer
len int
}
func main() {
var l []string
for n := 0; n < 1000; n++ {
l = append(l, strings.Repeat("x", 1000))
}
l = l[:0]
l = append(l, strings.Repeat("y", 1001))
over := (cap(l) - len(l)) * int(unsafe.Sizeof(stringStruct{}))
for i, o := len(l), l[:cap(l)]; i < cap(l); i++ {
over += len(o[i])
}
fmt.Println(over) // 1015368 bytes 64-bit, 1007184 bytes 32-bit
}
Playground: https://play.golang.org/p/Fi7EgbvdVkp
For a program to be correct, it must be readable. First, write the fundamental algorithms without the distraction of errors or special cases.
var l []string
for _, s := range a {
if len(l[0]) <= len(s) {
if len(l[0]) < len(s) {
l = l[:0]
}
l = append(l, s)
}
}
Next, add special cases, without disrupting the flow of the fundamental algorithm. In this case, handle zero- and one-length lists.
var l []string
if len(a) > 0 {
l = append(l, a[0])
a = a[1:]
}
for _, s := range a {
if len(l[0]) <= len(s) {
if len(l[0]) < len(s) {
l = l[:0]
}
l = append(l, s)
}
}
Finally, ensure that the function is efficient for both CPU and memory. The allocation is precise and there are no dangling pointers to unused strings.
var l []string
if len(a) > 0 {
l = append(l, a[0])
a = a[1:]
}
for _, s := range a {
if len(l[0]) <= len(s) {
if len(l[0]) < len(s) {
l = l[:0]
}
l = append(l, s)
}
}
return append([]string(nil), l...)

Print matrix as string in golang

I have a matrix of integers, represented by a multivariate array. I'm trying to concatenate the numbers into a string representation, rows-by-columns. My naive approach is to walk over all entries in the matrix and append them to a nullstring.
However, I'm getting an error that my append function is saying:
./main.go:xx:yy: first argument to append must be slice; have string
My code is:
type MatString string
type IntMat [3][3]Int // external constraints require fixed size, symmetric.
func Matrix2String(t IntMat) MatString {
// s var string
s := ""
for i := range t {
for j := range t[i] {
s = append(s[:], fmt.Sprintf("%s", j))
// fmt.Sprintf(s)
}
}
return MatString(s)
}
What am I misunderstanding about arrays, slices, and joins, and how can I iteratively build up this string correctly?
Collect the elements in a slice of strings. Join the slice to produce the result.
func Matrix2String(t IntMat) MatString {
var s []string
for i := range t {
for _, n := range t[i] {
s = append(s, fmt.Sprintf("%d", n))
}
}
return MatString(strings.Join(s, ""))
}
Another approach is to build the string in a []byte and convert at the end:
func Matrix2String(t IntMat) MatString {
var s []byte
for i := range t {
for _, n := range t[i] {
s = strconv.AppendInt(s, int64(n), 10)
}
}
return MatString(s)
}
I didn't include any delimiters because the question didn't include them.
You can simply concatenate converted integers to strings, to the response
func Matrix2String(t IntMat) MatString {
s := ""
for i := range t {
for _, n := range t[i] {
s += fmt.Sprintf("%d", n)
}
}
return MatString(s)
}
Playground

Resources