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...)
Related
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
}
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
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()
}
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
I have been giving HackerRank a try where the problems often require reading lines of integers into arrays (slices).
For many of the problems, my parsing code ends up being larger than the algorithmic meat of the solution. For instance, that was the case in Sherlock and Array
Any ideas on how to concisely parse a space-separated line of integers into a slice? fmt.Scanf doesn't support slices and when using bufio I get long solutions.
Some requirements:
You can only use the standard library.
The solution should be concise, the shorter the better.
Error checks shouldn't be skipped. I know that the input is well defined in HackerRank and you should be able to cut corners, but please don't, it's bad practice.
It should be reasonably efficient.
NOTE:
The parser should only consume a single line and not the full input.
Well, I have done some hackerrank problems too, and here is what I came up with. Typically, problems start with the number of items in the array:
func main() {
var N int
fmt.Scanf("%d", &N)
line := make([]int, N)
for i, _ := range line {
fmt.Scanf("%d", &line[i])
}
// Do something with the values
}
// inputs space separated list of integers, outputs []int64
package main
import (
"bufio"
"fmt"
"strconv"
"strings"
)
func main() {
fmt.Println(parse("100 200 300"))
}
func parse(i string) (o []int64) {
// from https://golang.org/pkg/bufio/#example_Scanner_custom
s := bufio.NewScanner(strings.NewReader(i))
splitter := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
advance, token, err = bufio.ScanWords(data, atEOF)
if err == nil && token != nil {
x, err := strconv.ParseInt(string(token), 10, 32)
if err != nil {
panic(err)
}
o = append(o, x)
}
return
}
s.Split(splitter)
for s.Scan() {
}
return o
}
You can use fmt.Scanf, but you need to keep track of the values you're getting.
// a.go
package main
import (
"fmt"
"io"
)
func main() {
var (
next int
nums []int
)
for {
n, err := fmt.Scanf("%d", &next)
if err == io.EOF {
break
}
if err != nil {
panic(err)
}
if n == 0 {
break
}
nums = append(nums, next)
}
fmt.Printf("%#v\n", nums)
}
$ echo "4 8 15 16 23 42" | go run a.go
[]int{4, 8, 15, 16, 23, 42}
1) read string
2) prepend [ and append ]
3) parse as json into []int?
var input = "1,2,3"
var answer []int
j := []byte(fmt.Sprintf("[%s]",input))
err:= json.Unmarshal(j, &input)
if err != nil {
panic(err)
}
for k,v := range input {
fmt.Printf("Element №%v is %v\n", k,v)
}
also using split strings (https://godoc.org/strings#Split)
and https://godoc.org/strconv#ParseInt
input:= "1,2,3"
temp := strings.Split(input, ",")
var answer []int
for _,v := range temp {
i,err := strconv.ParseInt(v)
if err != nill {
panic(err)
}
answer = append(answer, i)
}
UPD: just found that the numbers are SPACE separated.
So, this code have to do the thing:
input:= "1 2 3"
temp := strings.Split(input, " ")
var answer []int
for _,v := range temp {
i,err := strconv.ParseInt(v)
if err != nill {
panic(err)
}
answer = append(answer, i)
}
func main() {
arr := make([]int, 0)
reader := bufio.NewReaderSize(os.Stdin, 1024*1024)
for {
line, _, err := reader.ReadLine()
if err != nil {
if err == io.EOF {
break
}
panic(err)
}
str := strings.Split(string(line), " ")
for i:=0; i<len(str); i++ {
v, _ := strconv.Atoi(str[i])
arr = append(arr, v)
}
}
fmt.Println(arr)
}
I've used this for those times playing in hackerrank (so concise, but not tested for humans):
func scanInt(n int) []int {
input := make([]int, n)
buffer := make([]interface{}, n)
for i := 0; i < n; i++ {
buffer[i] = &input[i]
}
fmt.Scanln(buffer...)
return input
}