Idiomatic else if chain for a map key - go

I have a fairly long else if chain looking for keys in a map and setting a specific value if found. The way I chose to do it with the least amount of code was this (m is a map[string]interface{})
var ok bool
var s interface{}
if s, ok = m["key1"]; ok {
} else if s, ok = m["key2"]; ok {
....
} else if s, ok = m["keyN"]; ok {
} else {
return RuhRohError
}
g.Id = s.(string)
This feels kinda clunky, I am doing all of these else if's to set a variable in the condition. Is there an idiomatic way to do this? I think this way doesn't make it immediately obvious what I am trying to do.

For example,
package main
import "fmt"
func findValue(m map[string]interface{}, keys []string) (interface{}, bool) {
for _, key := range keys {
if value, ok := m[key]; ok {
return value, true
}
}
return nil, false
}
func main() {
m := map[string]interface{}{"keyn": "valuen"}
keys := []string{"key1", "key2", "keyn"}
s, found := findValue(m, keys)
if !found {
return
}
id := s.(string)
fmt.Println(id)
}
Output:
valuen

Actually I think the code you provided is very easy to read and understand. It is a bit verbose but there is no magic ;). So if there is only one place where you have to write this code I would leave it as it is. If you need to write it a few times I would consider writing findValue function mentioned in another comment.

Related

Using default value in golang func

I'm trying to implement a default value according to the option 1 of the post Golang and default values. But when I try to do go install the following error pops up in the terminal:
not enough arguments in call to test.Concat1
have ()
want (string)
Code:
package test
func Concat1(a string) string {
if a == "" {
a = "default-a"
}
return fmt.Sprintf("%s", a)
}
// other package
package main
func main() {
test.Concat1()
}
Thanks in advance.
I don't think what you are trying to do will work that way. You may want to opt for option #4 from the page you cited, which uses variadic variables. In your case looks to me like you want just a string, so it'd be something like this:
func Concat1(a ...string) string {
if len(a) == 0 {
return "a-default"
}
return a[0]
}
Go does not have optional defaults for function arguments.
You may emulate them to some extent by having a special type
to contain the set of parameters for a function.
In your toy example that would be something like
type Concat1Args struct {
a string
}
func Concat1(args Concat1Args) string {
if args.a == "" {
args.a = "default-a"
}
return fmt.Sprintf("%s", args.a)
}
The "trick" here is that in Go each type has its respective
"zero value", and when producing a value of a composite type
using the so-called literal, it's possible to initialize only some of the type's fields, so in our example that would be
s := Concat1(Concat1Args{})
vs
s := Concat1(Concat1Args{"whatever"})
I know that looks clumsy, and I have showed this mostly for
demonstration purpose. In real production code, where a function
might have a dozen of parameters or more, having them packed
in a dedicate composite type is usually the only sensible way
to go but for a case like yours it's better to just explicitly
pass "" to the function.
Golang does not support default parameters. Accordingly, variadic arguments by themselves are not analogous. However, variadic functions with the use of error handling can 'resemble' the pattern. Try the following as a simple example:
package main
import (
"errors"
"log"
)
func createSeries(p ...int) ([]int, error) {
usage := "Usage: createSeries(<length>, <optional starting value>), length should be > 0"
if len(p) == 0 {
return nil, errors.New(usage)
}
n := p[0]
if n <= 0 {
return nil, errors.New(usage)
}
var base int
if len(p) == 2 {
base = p[1]
} else if len(p) > 2 {
return nil, errors.New(usage)
}
vals := make([]int, n)
for i := 0; i < n; i++ {
vals[i] = base + i
}
return vals, nil
}
func main() {
answer, err := createSeries(4, -9)
if err != nil {
log.Fatal(err)
}
log.Println(answer)
}
Default parameters work differently in Go than they do in other languages. In a function there can be one ellipsis, always at the end, which will keep a slice of values of the same type so in your case this would be:
func Concat1(a ...string) string {
but that means that the caller may pass in any number of arguments >= 0. Also you need to check that the arguments in the slice are not empty and then assign them yourself. This means they do not get assigned a default value through any kind of special syntax in Go. This is not possible but you can do
if a[0] == "" {
a[0] = "default value"
}
If you want to make sure that the user passes either zero or one strings, just create two functions in your API, e.g.
func Concat(a string) string { // ...
func ConcatDefault() string {
Concat("default value")
}

Should true or false terminate callback iteration?

In some languages it's necessary or cleaner to do iteration by providing a callback function that receives items and returns a boolean that indicates whether to continue or stop the iteration.
Which is the preferred value to indicate desire to stop/continue? Why? What precedents exist?
Example in Go:
func IntSliceEach(sl []int, cb func(i int) (more bool)) (all bool) {
for _, i := range sl {
if !cb(i) {
return false
}
}
return true
}
Which is the preferred value to indicate desire to stop/continue?
true for continue
Why?
Example 1:
func example(i interface{}) {
if w, ok := i.(io.Writer); ok {
// do something with your writer, ok indicates that you can continue
}
}
Example 2:
var sum int = 0
it := NewIntStatefulIterator(int_data)
for it.Next() {
sum += it.Value()
}
In both cases true (ok) indicates that you should continue. So I assume that it would be way to go in your example.
Foreword: The following answer applies to a callback function which decides based on the current item(s) whether the loop should terminate early - this is what you asked.
This is not to be confused with a function that progresses and reports if there are more elements to process, where a true return value is generally accepted to signal that there are more elements (for which a good example is Scanner.Scan()), and whose typical use is:
scanner := bufio.NewScanner(input)
for scanner.Scan() {
// Process current item (line):
line := scanner.Text()
fmt.Println(line) // Do something with line
}
Sticking to bool return type
Usually returning true to indicate termination results in code that is easier to read. This is due to the nature of for: if you do nothing, for continues, so you have to explicitly break if you want to terminate early, so having a clean termination condition is more common.
But it's a matter of taste. You may go whichever you like, but what's important is to name your callback function in a meaningful way that will clearly state what its return value means, and thus looking at the code (the condition in which it is used) will be easily understandable.
For example the following names are good and the return value is unambiguous:
// A return value of true means to terminate
func isLast(item Type) bool
func terminateAfter(item Type) bool
func abort(item Type) bool
// A return value of true means to continue (not to terminate)
func keepGoing(item Type) bool
func carryOn(item Type) bool
func processMore(item Type) bool
Using these results in easily understandable code:
for i, v := range vals {
doSomeWork()
if terminateAfter(v) {
break // or return
}
}
for i, v := range vals {
doSomeWork()
if !keepGoing(v) {
break // or return
}
}
// Or an alternative to the last one (subjective which is easier to read):
for i, v := range vals {
doSomeWork()
if keepGoing(v) {
continue
}
break
}
As negative examples, the following callback function names are bad as you can't guess what their return value mean:
// Bad: you can't tell what return value of true means just by its name:
func test(item Type) bool
func check(item Type) bool
Having error return type
It's also common for the callback to not just test but also do some work with the passed item. In these cases it is meaningful to return an error instead of a bool. Doing so, obviously the nil return value indicates success (and to continue), and a non-nil value indicates error and that processing should stop.
func process(item Type) error
for i, v := range vals {
if err := process(v); err != nil {
// Handle error and terminate
break
}
}
Having enum-like return value
Also if multiple return values have meaning, you may choose to define constants for return values, which you can name meaningfully.
type Action int
const (
ActionContinue Action = iota
ActionTerminate
ActionSkip
)
func actionToTake(item Type) Action
for i, v := range vals {
switch actionToTake(v) {
case ActionSkip:
continue
case ActionTerminate:
return
}
doSomeWork()
}

Is there a way to write generic code to find out whether a slice contains specific element in Go?

I want to know is there a generic way to write code to judge whether a slice contains an element, I find it will frequently useful since there is a lot of logic to fist judge whether specific elem is already in a slice and then decide what to do next. But there seemed not a built-in method for that(For God's sake, why?)
I try to use interface{} to do that like:
func sliceContains(slice []interface{}, elem interface{}) bool {
for _, item := range slice {
if item == elem {
return true
}
}
return false
}
I thought interface{} is sort of like Object of Java, but apparently, I was wrong. Should I write this every time meet with a new struct of slice? Isn't there a generic way to do this?
You can do it with reflect, but it will be MUCH SLOWER than a non-generic equivalent function:
func Contains(slice, elem interface{}) bool {
sv := reflect.ValueOf(slice)
// Check that slice is actually a slice/array.
// you might want to return an error here
if sv.Kind() != reflect.Slice && sv.Kind() != reflect.Array {
return false
}
// iterate the slice
for i := 0; i < sv.Len(); i++ {
// compare elem to the current slice element
if elem == sv.Index(i).Interface() {
return true
}
}
// nothing found
return false
}
func main(){
si := []int {3, 4, 5, 10, 11}
ss := []string {"hello", "world", "foo", "bar"}
fmt.Println(Contains(si, 3))
fmt.Println(Contains(si, 100))
fmt.Println(Contains(ss, "hello"))
fmt.Println(Contains(ss, "baz"))
}
How much slower? about x50-x60 slower:
Benchmarking against a non generic function of the form:
func ContainsNonGeneic(slice []int, elem int) bool {
for _, i := range slice {
if i == elem {
return true
}
}
return false
}
I'm getting:
Generic: N=100000, running time: 73.023214ms 730.23214 ns/op
Non Generic: N=100000, running time: 1.315262ms 13.15262 ns/op
You can make it using the reflect package like that:
func In(s, e interface{}) bool {
slice, elem := reflect.ValueOf(s), reflect.ValueOf(e)
for i := 0; i < slice.Len(); i++ {
if reflect.DeepEqual(slice.Index(i).Interface(), elem.Interface()) {
return true
}
}
return false
}
Playground examples: http://play.golang.org/p/TQrmwIk6B4
Alternatively, you can:
define an interface and make your slices implement it
use maps instead of slices
just write a simple for loop
What way to choose depends on the problem you are solving.
I'm not sure what your specific context is, but you'll probably want to use a map to check if something already exists.
package main
import "fmt"
type PublicClassObjectBuilderFactoryStructure struct {
Tee string
Hee string
}
func main() {
// Empty structs occupy zero bytes.
mymap := map[interface{}]struct{}{}
one := PublicClassObjectBuilderFactoryStructure{Tee: "hi", Hee: "hey"}
two := PublicClassObjectBuilderFactoryStructure{Tee: "hola", Hee: "oye"}
three := PublicClassObjectBuilderFactoryStructure{Tee: "hi", Hee: "again"}
mymap[one] = struct{}{}
mymap[two] = struct{}{}
// The underscore is ignoring the value, which is an empty struct.
if _, exists := mymap[one]; exists {
fmt.Println("one exists")
}
if _, exists := mymap[two]; exists {
fmt.Println("two exists")
}
if _, exists := mymap[three]; exists {
fmt.Println("three exists")
}
}
Another advantage of using maps instead of a slice is that there is a built-in delete function for maps. https://play.golang.org/p/dmSyyryyS8
If you want a rather different solution, you might try the code-generator approach offered by tools such as Gen. Gen writes source code for each concrete class you want to hold in a slice, so it supports type-safe slices that let you search for the first match of an element.
(Gen also offers a few other kinds of collection and allows you to write your own.)

How to do one-liner if else statement? [duplicate]

This question already has answers here:
What is the idiomatic Go equivalent of C's ternary operator?
(14 answers)
Closed 1 year ago.
Please see https://golangdocs.com/ternary-operator-in-golang as pointed by #accdias (see comments)
Can I write a simple if-else statement with variable assignment in go (golang) as I would do in php? For example:
$var = ( $a > $b )? $a: $b;
Currently I have to use the following:
var c int
if a > b {
c = a
} else {
c = b
}
Sorry I cannot remember the name if this control statement and I couldn't find the info in-site or through google search. :/
As the comments mentioned, Go doesn't support ternary one liners. The shortest form I can think of is this:
var c int
if c = b; a > b {
c = a
}
But please don't do that, it's not worth it and will only confuse people who read your code.
As the others mentioned, Go does not support ternary one-liners. However, I wrote a utility function that could help you achieve what you want.
// IfThenElse evaluates a condition, if true returns the first parameter otherwise the second
func IfThenElse(condition bool, a interface{}, b interface{}) interface{} {
if condition {
return a
}
return b
}
Here are some test cases to show how you can use it
func TestIfThenElse(t *testing.T) {
assert.Equal(t, IfThenElse(1 == 1, "Yes", false), "Yes")
assert.Equal(t, IfThenElse(1 != 1, nil, 1), 1)
assert.Equal(t, IfThenElse(1 < 2, nil, "No"), nil)
}
For fun, I wrote more useful utility functions such as:
IfThen(1 == 1, "Yes") // "Yes"
IfThen(1 != 1, "Woo") // nil
IfThen(1 < 2, "Less") // "Less"
IfThenElse(1 == 1, "Yes", false) // "Yes"
IfThenElse(1 != 1, nil, 1) // 1
IfThenElse(1 < 2, nil, "No") // nil
DefaultIfNil(nil, nil) // nil
DefaultIfNil(nil, "") // ""
DefaultIfNil("A", "B") // "A"
DefaultIfNil(true, "B") // true
DefaultIfNil(1, false) // 1
FirstNonNil(nil, nil) // nil
FirstNonNil(nil, "") // ""
FirstNonNil("A", "B") // "A"
FirstNonNil(true, "B") // true
FirstNonNil(1, false) // 1
FirstNonNil(nil, nil, nil, 10) // 10
FirstNonNil(nil, nil, nil, nil, nil) // nil
FirstNonNil() // nil
If you would like to use any of these, you can find them here https://github.com/shomali11/util
I often use the following:
c := b
if a > b {
c = a
}
basically the same as #Not_a_Golfer's but using type inference.
Thanks for pointing toward the correct answer.
I have just checked the Golang FAQ (duh) and it clearly states, this is not available in the language:
Does Go have the ?: operator?
There is no ternary form in Go. You may use the following to achieve the same result:
if expr {
n = trueVal
} else {
n = falseVal
}
additional info found that might be of interest on the subject:
Rosetta Code for Conditional Structures in Go
Ternary Operator in Go experiment from this guy
One possible way to do this in just one line by using a map, simple I am checking whether a > b if it is true I am assigning c to a otherwise b
c := map[bool]int{true: a, false: b}[a > b]
However, this looks amazing but in some cases it might NOT be the perfect solution because of evaluation order. For example, if I am checking whether an object is not nil get some property out of it, look at the following code snippet which will panic in case of myObj equals nil
type MyStruct struct {
field1 string
field2 string
}
var myObj *MyStruct
myObj = nil
myField := map[bool]string{true: myObj.field1, false: "empty!"}[myObj != nil}
Because map will be created and built first before evaluating the condition so in case of myObj = nil this will simply panic.
Not to forget to mention that you can still do the conditions in just one simple line, check the following:
var c int
...
if a > b { c = a } else { c = b}
A very similar construction is available in the language
**if <statement>; <evaluation> {
[statements ...]
} else {
[statements ...]
}*
*
i.e.
if path,err := os.Executable(); err != nil {
log.Println(err)
} else {
log.Println(path)
}
Use lambda function instead of ternary operator
Example 1
to give the max int
package main
func main() {
println( func(a,b int) int {if a>b {return a} else {return b} }(1,2) )
}
Example 2
Suppose you have this must(err error) function to handle errors and you want to use it when a condition isn't fulfilled.
(enjoy at https://play.golang.com/p/COXyo0qIslP)
package main
import (
"errors"
"log"
"os"
)
// must is a little helper to handle errors. If passed error != nil, it simply panics.
func must(err error) {
if err != nil {
log.Println(err)
panic(err)
}
}
func main() {
tmpDir := os.TempDir()
// Make sure os.TempDir didn't return empty string
// reusing my favourite `must` helper
// Isn't that kinda creepy now though?
must(func() error {
var err error
if len(tmpDir) > 0 {
err = nil
} else {
err = errors.New("os.TempDir is empty")
}
return err
}()) // Don't forget that empty parentheses to invoke the lambda.
println("We happy with", tmpDir)
}
Sometimes, I try to use anonymous function to achieve defining and assigning happen at the same line. like below:
a, b = 4, 8
c := func() int {
if a >b {
return a
}
return b
} ()
https://play.golang.org/p/rMjqytMYeQ0
Like user2680100 said, in Golang you can have the structure:
if <statement>; <evaluation> {
[statements ...]
} else {
[statements ...]
}
This is useful to shortcut some expressions that need error checking, or another kind of boolean checking, like:
var number int64
if v := os.Getenv("NUMBER"); v != "" {
if number, err = strconv.ParseInt(v, 10, 64); err != nil {
os.Exit(42)
}
} else {
os.Exit(1)
}
With this you can achieve something like (in C):
Sprite *buffer = get_sprite("foo.png");
Sprite *foo_sprite = (buffer != 0) ? buffer : donut_sprite
But is evident that this sugar in Golang have to be used with moderation, for me, personally, I like to use this sugar with max of one level of nesting, like:
var number int64
if v := os.Getenv("NUMBER"); v != "" {
number, err = strconv.ParseInt(v, 10, 64)
if err != nil {
os.Exit(42)
}
} else {
os.Exit(1)
}
You can also implement ternary expressions with functions like func Ternary(b bool, a interface{}, b interface{}) { ... } but i don't like this approach, looks like a creation of a exception case in syntax, and creation of this "features", in my personal opinion, reduce the focus on that matters, that is algorithm and readability, but, the most important thing that makes me don't go for this way is that fact that this can bring a kind of overhead, and bring more cycles to in your program execution.
You can use a closure for this:
func doif(b bool, f1, f2 func()) {
switch{
case b:
f1()
case !b:
f2()
}
}
func dothis() { fmt.Println("Condition is true") }
func dothat() { fmt.Println("Condition is false") }
func main () {
condition := true
doif(condition, func() { dothis() }, func() { dothat() })
}
The only gripe I have with the closure syntax in Go is there is no alias for the default zero parameter zero return function, then it would be much nicer (think like how you declare map, array and slice literals with just a type name).
Or even the shorter version, as a commenter just suggested:
func doif(b bool, f1, f2 func()) {
switch{
case b:
f1()
case !b:
f2()
}
}
func dothis() { fmt.Println("Condition is true") }
func dothat() { fmt.Println("Condition is false") }
func main () {
condition := true
doif(condition, dothis, dothat)
}
You would still need to use a closure if you needed to give parameters to the functions. This could be obviated in the case of passing methods rather than just functions I think, where the parameters are the struct associated with the methods.
As everyone else pointed out, there's no ternary operator in Go.
For your particular example though, if you want to use a single liner, you could use Max.
import "math"
...
c := math.Max(a, b)
Ternary ? operator alternatives | golang if else one line
You can’t write a short one-line conditional in Go language ; there is no ternary conditional operator.
Read more about if..else of Golang

How to use OR operator properly in Golang

How can i do this simplified in Golang
var planningDate string
date, ok := data["planningDate"]
if !ok {
planningDate = util.TimeStamp()
} else {
planningDate = date
}
Thanx
I don't see any way to do this in a single line, as there is no ternary operator in Go. You cannot use | either as operands are not numbers. However, here is a solution in three lines (assuming date was just a temporary variable):
planningDate, ok := data["planningDate"]
if !ok {
planningDate = util.TimeStamp()
}
You can do something like:
func T(exp bool, a, b interface{}) interface{} {
if exp {
return a
}
return b
}
and use it whenever you want, like a ternary-operator:
planningDate = T((ok), date, util.TimeStamp())

Resources