How do I create a slice of one item in Go? - go

Let's say a function takes a slice of strings:
func Join(strs []string) {
...
}
I have a single string:
a := "y'all ain't got the honey nut?"
How can I convert that string into a slice?

You can create a slice of one item using the following convention:
a := "y'all ain't got the honey nut?"
singleItemArray := []string{a}
strings.Join(singleItemArray);

The actual answer to your question is as simple as []string{"string"}, as miltonb said.
But what I wanted to point out is how easy it is to write and use a variadic function in Go, a function with a variable number of arguments.
You can change signature of your function to F(a ...string). Then, a is slice in the function F, and you can call it like F("a") and F("a", "b"). And when you actually have a slice or array, you can pass it to F by calling F(a...).
Not sure if this syntax fits your job, but I wanted to let you know about it as an option.

The question as phrased actually references Arrays and Slices. The question text is about an array and the code is illustrating using a slice. Therefore there two questions are implied; pass a single item slice, and pass a single item array.
An array: var a [1]string
A slice: var s []string
Passing a single item slice to the function:
func SliceFunc( slc []string) {
fmt.Println(slc)
}
func main() {
a := "stringy"
SliceFunc( []string{a} )
// or an actual array to the same function
b := [...]string { "thingy" }
SliceFunc( []string{b[0] )
}
Passing a single item array to the function.
Here there is an issue, as an array has a fixed length and as a parameter to a function it cannot accept different length arrays so we are left with working function which has limited flexibility:
func ArrayFunc( arr [1]string) {
fmt.Println(slc)
}
func main() {
var a [1]string
a[0] = "stringy"
ArrayFunc( a )
}
It seems that as a generalization sticking to slices is a more flexible solution.
(If you would like more on Slices and Arrays here one blog by Andrew Gerrand covering go slices usage and internals.)

You can utilize append or make:
package main
import "fmt"
func main() {
{
var a []string
a = append(a, "north")
fmt.Println(a)
}
{
a := make([]string, 1)
a[0] = "north"
fmt.Println(a)
}
}
https://golang.org/pkg/builtin

Related

Right way to assert that slice of pointers to strings contains expected strings?

Is there an easy and compact way using Testify to assert that a slice of pointers to strings contains a pointer to a string that matches my expectation?
Imagine that you're getting a slice of pointers to strings back from a function call (maybe from an API), and you'd like to validate that it contains pointers to the strings that you'd expect. To simulate that, I'll just make a test data structure to illustrate my point:
// Shared Fixture
var one = "one"
var two = "two"
var three = "three"
var slice = []*string{&one, &two, &three}
Now I want to write a test that asserts the slice contains an expected value. I could write this test:
func TestSliceContainsString(t *testing.T) {
assert.Contains(t, slice, "one")
}
It doesn't work: []*string{(*string)(0x22994f0), (*string)(0x2299510), (*string)(0x2299500)} does not contain "one". Makes sense, the slice contains pointers to strings, and the string "one" is not one of those pointers.
I could convert it first. It takes more code, but it works:
func TestDereferencedSliceContainsString(t *testing.T) {
deref := make([]string, len(slice))
for i, v := range slice {
deref[i] = *v
}
assert.Contains(t, deref, "one")
}
I can also pass a pointer to a string as my expectation:
func TestSliceContainsPointerToExpectation(t *testing.T) {
expect := "one"
assert.Same(t, &one, &one)
assert.NotSame(t, &one, &expect)
// How can I assert that they contain values
assert.Contains(t, slice, &expect)
}
Honestly, that's not bad. I can assert that a reference to a string (pointing to a difference memory location) contains the value that I expect. The main annoyance with this path is that I can't pass a reference to a literal, which would make it take less space:
func TestSliceContainsString(t *testing.T) {
assert.Contains(t, slice, &"one")
}
Is there another approach that I'm not considering? Is one of these more idiomatic of golang/testify?
Yes, unfortunately the &"one" syntax isn't valid (a few years ago, I opened an issue to allow that syntax; it was closed, though Rob Pike opened a similar issue more recently).
For now, I think the best approach is to just take the address of a variable, as in your TestSliceContainsPointerToExpectation. Or, if you're doing this often, you can write a simple stringPtr function so you can do it as a one-liner:
func stringPtr(value string) *string {
return &value
}
func TestSliceContainsString(t *testing.T) {
assert.Contains(t, slice, stringPtr("one"))
}
Or, if you're using at least Go 1.18 (with generics), you can make a generic ptr function:
func ptr[T any](value T) *T {
return &value
}
func TestSliceContains(t *testing.T) {
assert.Contains(t, slice, ptr("one"))
}
See these in the Go Playground.

Converting []*string to []string in golang

i'm new to go.
i have a function that accepts []string as input, but i the input i have to pass is an []*string, how do i convert []*string to []string.
is there any way to convert it using any utilities, or do i have to iterate it using a for-loop and construct an array?
Playground link https://play.golang.org/p/_s2g7-IfGAy
package main
import (
"fmt"
)
func main() {
//Declaring an array of []*string and assigning value to it
var a [1]*string
var strPointer = new(string)
*strPointer = "1"
a[0] = strPointer
fmt.Println(*a[0])
// accept(a) this wont work
//Converting to array []string
var b []string
for i := range a {
b = append(b, *a[i])
}
accept(b)// this works
fmt.Println(b)
}
func accept(param []string) {
fmt.Println("Works!")
}
Your accept(param []string) expect a slice of string.
var a [1]*string This declares Go array with a length of 1. So it's not a slice.
You can declare an empty slice of string pointers using this. var a []*string
And you have to iterate through the array and make a slice with the value of pointer elements and call the accept with that slice.
Example function to convert []*string to []string
func stringer(str []*string) []string{
var strs []string
for _, s := range str {
if s == nil {
strs = append(strs, "")
continue
}
strs = append(strs, *s)
}
return strs
}
run here
how do i convert []*string to []string
You cannot. This kind of type conversion not possible in Go.
is there any way to convert it using any utilities [...]
Asking for 3rd party libraries/tools/packages is OT on SO.
[...] or do i have to iterate it using a for-loop and construct an array
This is the only clean, normal, "idiomatic" way of doing this.

Why [capacity]string assert to []string will be failed in Golang?

I am using Golang1.14.
Here is the test code.
package main
import "time"
func checkData(data interface{}) {
if _, ok := data.([]string); ok {
println("Assert true.")
} else {
println("Assert false.")
}
}
func main() {
var years [20]string
for i := 0; i < 20; i++ {
years[i] = string(time.Now().Year() - 10 + i)
}
checkData(years)
foods := []string{"Fruit", "Grass", "Fish", "Meat"}
checkData(foods)
}
The output is:
Assert false.
Assert true.
I am new to Golang and really confusing that [20]string is not a []string.Can someone tell me why?Thanks.
[20]string is an array. It is a type that contains 20 strings, and if you pass it as an interface{}, you can recover it using intf.([20]string).
[]string is a slice. It has a backing array, but it is essentially a view over an array. You assertion checks if the interface is a slice, so this one works.
Arrays and slices are different things in Go. An array is a data type with a fixed size. For instance:
func f(arr [10]int) {...}
You can only call f with an int array of size 10. When you do call it, the array will be passes as value, so the function will get a copy of the array, all 10 members of it. But:
func f(arr []int) {...}
You can call f with any size of slice. A slice contains a reference to its underlying array, so an array copy will not take place here. You cannot call thisf` with an array.

Interface conversion in slice of slice

I wrote this example code (https://play.golang.org/p/u_oz5X4aU07):
func main() {
var obj interface{}
json.Unmarshal([]byte("[[1,2],[3,4]]"), &obj)
val := obj.([][]int)
fmt.Println(val)
}
Why I get the error:
interface conversion: interface {} is []interface {}, not [][]int
Is there a simple way to transform obj in a slice of slice?
This code works, but I'd like something more compact and efficient.
var val [][]float64
for r, v := range obj.([]interface{}) {
val = append(val,nil)
for _, w := range v.([]interface{}) {
val[r] = append(val[r], w.(float64))
}
}
No, ultimately you'll have to loop over the two slices!
You can read here why you can't just use one as the other:
https://research.swtch.com/interfaces
This answer might also be useful:
Why golang struct array cannot be assigned to an interface array
Essentially it's because the interface is stored as a 2 word pair, one defining the type and one the values.
You have to manually convert to the required type by visiting all the values in for-range loops.

Can we write a generic array/slice deduplication in go?

Is there a way to write a generic array/slice deduplication in go, for []int we can have something like (from http://rosettacode.org/wiki/Remove_duplicate_elements#Go ):
func uniq(list []int) []int {
unique_set := make(map[int] bool, len(list))
for _, x := range list {
unique_set[x] = true
}
result := make([]int, len(unique_set))
i := 0
for x := range unique_set {
result[i] = x
i++
}
return result
}
But is there a way to extend it to support any array? with a signature like:
func deduplicate(a []interface{}) []interface{}
I know that you can write that function with that signature, but then you can't actually use it on []int, you need to create a []interface{} put everything from the []int into it, pass it to the function then get it back and put it into a []interface{} and go through this new array and put everything in a new []int.
My question is, is there a better way to do this?
While VonC's answer probably does the closest to what you really want, the only real way to do it in native Go without gen is to define an interface
type IDList interface {
// Returns the id of the element at i
ID(i int) int
// Returns the element
// with the given id
GetByID(id int) interface{}
Len() int
// Adds the element to the list
Insert(interface{})
}
// Puts the deduplicated list in dst
func Deduplicate(dst, list IDList) {
intList := make([]int, list.Len())
for i := range intList {
intList[i] = list.ID(i)
}
uniques := uniq(intList)
for _,el := range uniques {
dst.Insert(list.GetByID(el))
}
}
Where uniq is the function from your OP.
This is just one possible example, and there are probably much better ones, but in general mapping each element to a unique "==able" ID and either constructing a new list or culling based on the deduplication of the IDs is probably the most intuitive way.
An alternate solution is to take in an []IDer where the IDer interface is just ID() int. However, that means that user code has to create the []IDer list and copy all the elements into that list, which is a bit ugly. It's cleaner for the user to wrap the list as an ID list rather than copy, but it's a similar amount of work either way.
The only way I have seen that implemented in Go is with the clipperhouse/gen project,
gen is an attempt to bring some generics-like functionality to Go, with some inspiration from C#’s Linq and JavaScript’s underscore libraries
See this test:
// Distinct returns a new Thing1s slice whose elements are unique. See: http://clipperhouse.github.io/gen/#Distinct
func (rcv Thing1s) Distinct() (result Thing1s) {
appended := make(map[Thing1]bool)
for _, v := range rcv {
if !appended[v] {
result = append(result, v)
appended[v] = true
}
}
return result
}
But, as explained in clipperhouse.github.io/gen/:
gen generates code for your types, at development time, using the command line.
gen is not an import; the generated source becomes part of your project and takes no external dependencies.
You could do something close to this via an interface. Define an interface, say "DeDupable" requiring a func, say, UniqId() []byte, which you could then use to do the removing of dups. and your uniq func would take a []DeDupable and work on it

Resources