In Go 1.18 strings.Title() is deprecated. What to use now? And how? - go

As suggested here names of people should be capitalized like John William Smith.
I'm writing a small software in Golang which gets last and first name from user's form inputs.
Until Go 1.18 I was using:
lastname = strings.Title(strings.ToLower(strings.TrimSpace(lastname)))
firstname = strings.Title(strings.ToLower(strings.TrimSpace(firstname)))
It works but now Go 1.18 has deprecated strings.Title().
They suggest to use golang.org/x/text/cases instead.
So I think I should change my code in something like this:
caser := cases.Title(language.Und)
lastname = caser.Title(strings.ToLower(strings.TrimSpace(lastname)))
firstname = caser.Title(strings.ToLower(strings.TrimSpace(firstname)))
It works the same as before.
The difference is for Dutch word like ijsland that should be titled as IJsland and not Ijsland.
The question
In the line caser := cases.Title(language.Und) I'm using Und because I don't know what language Tag to use.
Should I use language.English or language.AmericanEnglish or other?
So far it was like strings.Title() was using Und or English?

As mentioned in documentation strings.Title is deprecated and you should use cases.Title instead.
Deprecated: The rule Title uses for word boundaries does not handle
Unicode punctuation properly. Use golang.org/x/text/cases instead.
Here is an example code of how to use it as from two perspectives:
// Straightforward approach
caser := cases.Title(language.BrazilianPortuguese)
titleStr := caser.String(str)
// Transformer interface aware approach
src := []byte(s)
dest := []byte(s) // dest can also be `dest := src`
caser := cases.Title(language.BrazilianPortuguese)
_, _, err := caser.Transform(dest, src, true)
Make sure to take a look on the transform.Transformer.Transform and cases.Caser in order to understand what each parameter and return values mean, as well as the tool's limitations. For example:
A Caser may be stateful and should therefore not be shared between
goroutines.
Regarding what language to use, you should be aware of their difference in the results, besides that, you should be fine with any choice. Here is a copy from 煎鱼's summary on the differences that cleared it for me:
Go Playground: https://go.dev/play/p/xp59r1BkC9L
func main() {
src := []string{
"hello world!",
"i with dot",
"'n ijsberg",
"here comes O'Brian",
}
for _, c := range []cases.Caser{
cases.Lower(language.Und),
cases.Upper(language.Turkish),
cases.Title(language.Dutch),
cases.Title(language.Und, cases.NoLower),
} {
fmt.Println()
for _, s := range src {
fmt.Println(c.String(s))
}
}
}
With the following output
hello world!
i with dot
'n ijsberg
here comes o'brian
HELLO WORLD!
İ WİTH DOT
'N İJSBERG
HERE COMES O'BRİAN
Hello World!
I With Dot
'n IJsberg
Here Comes O'brian
Hello World!
I With Dot
'N Ijsberg
Here Comes O'Brian

So far it was like strings.Title() was using Und or English?
strings.Title() works based on ASCII, where cases.Title() works based on Unicode, there is no way to get the exact same behavior.
Should I use language.English or language.AmericanEnglish or other?
language.English, language.AmericanEnglish and language.Und all seem to have the same Title rules. Using any of them should get you the closest to the original strings.Title() behavior as you are going to get.
The whole point of using this package with Unicode support is that it is objectively more correct. So pick a tag appropriate for your users.

strings.Title(str) was deprecated, should change to cases.Title(language.Und, cases.NoLower).String(str)
package main
import (
"fmt"
"strings"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)
func main() {
fmt.Println(strings.Title("abcABC")) // AbcABC
fmt.Println(cases.Title(language.Und, cases.NoLower).String("abcABC")) // AbcABC
}
Playground : https://go.dev/play/p/i0Eqh3QfxTx

Here is a straightforward example of how to capitalize the initial letter of each string value in the variable using the golang.org/x/text package.
package main
import (
"fmt"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)
func main() {
sampleStr := "with value lower, all the letters are lowercase. this is good for poetry perhaps"
caser := cases.Title(language.English)
fmt.Println(caser.String(sampleStr))
}
Output : With Value Lower, All The Letters Are Lowercase. This Is Good For Poetry Perhaps
Playground Example: https://go.dev/play/p/_J8nGVuhYC9

Related

How to convert the string representation of a Terraform set of strings to a slice of strings

I've a terratest where I get an output from terraform like so s := "[a b]". The terraform output's value = toset([resource.name]), it's a set of strings.
Apparently fmt.Printf("%T", s) returns string. I need to iterate to perform further validation.
I tried the below approach but errors!
var v interface{}
if err := json.Unmarshal([]byte(s), &v); err != nil {
fmt.Println(err)
}
My current implementation to convert to a slice is:
s := "[a b]"
s1 := strings.Fields(strings.Trim(s, "[]"))
for _, v:= range s1 {
fmt.Println("v -> " + v)
}
Looking for suggestions to current approach or alternative ways to convert to arr/slice that I should be considering. Appreciate any inputs. Thanks.
Actually your current implementation seems just fine.
You can't use JSON unmarshaling because JSON strings must be enclosed in double quotes ".
Instead strings.Fields does just that, it splits a string on one or more characters that match unicode.IsSpace, which is \t, \n, \v. \f, \r and .
Moeover this works also if terraform sends an empty set as [], as stated in the documentation:
returning [...] an empty slice if s contains only white space.
...which includes the case of s being empty "" altogether.
In case you need additional control over this, you can use strings.FieldsFunc, which accepts a function of type func(rune) bool so you can determine yourself what constitutes a "space". But since your input string comes from terraform, I guess it's going to be well-behaved enough.
There may be third-party packages that already implement this functionality, but unless your program already imports them, I think the native solution based on the standard lib is always preferrable.
unicode.IsSpace actually includes also the higher runes 0x85 and 0xA0, in which case strings.Fields calls FieldsFunc(s, unicode.IsSpace)
package main
import (
"fmt"
"strings"
)
func main() {
src := "[a b]"
dst := strings.Split(src[1:len(src)-1], " ")
fmt.Println(dst)
}
https://play.golang.org/p/KVY4r_8RWv6

Pick the random value from slice and print on cli

How to pick the random value from slice in golang and i need to display it to cli.I have string which i converted to string array by splitting it. Now i want to choose random string from string array and display to user in cli and i need to ask user to input that particular string which is displayed on screen
and compare the user entered input.
string randgen := ‘na gd tg er dd wq ff gen vf ws’
s:= String.split(randgen,””)
s = [“na”, “gd”, ”er”, “tg”, “er”, “dd”, “wq”, “ff”, “gen”, “vf”, “ws”]
There are some issues with your code. You shouldn't define the type when initializing a variable using :=.
Also, it's not recommended to depend on spaces to construct and split your slice, as it's not clear what will happen if for example you have multiple spaces, or a tab between the characters instead.
This is a minimal solution that 'just works'.
package main
import (
"fmt"
"math/rand"
"strings"
"time"
)
func main() {
randgen := `na gd tg er dd wq ff gen vf ws`
s := strings.Split(randgen, " ")
fmt.Println(s)
rand.Seed(time.Now().UnixNano())
randIdx := rand.Intn(len(s))
fmt.Println("Randomly selected slice value : ", s[randIdx])
}
I would suggest reading the rand package documentation for an explanation of what rand.Seed does. Also, take a look at the shuffle function available in rand, as it's suited to your problem, if you want to build a more robust solution.

Generalizing Concurrent Map Function with Empty Interface In GOlang

So I'm super new to Golang and seeing as the big buzz around the language seems to be concurrency I figured a good way to get my toes wet would be to write a generalized map function. In psudo code:
map(F funtion,A array){
return([F(k) for k in A])
}
And obviously I want the calculations for each F(k) occur concurrently. For organization I have a main file with my implementation and a supporting file Mr with my required definitions.
.
├── main.go
└── Mr
   └── Mr.go
Main is a simple test implementation which should convert an array of strings to an array of ints where each member of the result is the length of the corresponding string in the input array.
package main
import(
"fmt"
"./Mr"
)
func exfunc(i int, c chan int){
c<-i
}
func main(){
data := make(map[int]string)
data[1]="these"
data[2]="are"
data[3]="some"
data[4]="words"
data[5]="and a few more..."
f := func(w string)int{
return(len(w))
}
testMr := Mr.Map(f,data) // this is line 22 (matters later)
fmt.Println(testMr)
}
Which works great with my Mr.Map function given that I specify all the types explicitly.
package Mr
type result struct{
key,value int
}
func wrapper(f func(string) int,k int,v string, c chan result){
c <- result{k,f(v)}
}
func Map(f func(string) int,m map[int]string) map[int]int{
c := make(chan result)
ret := make(map[int]int)
n := 0
for k := range m{
go wrapper(f,k,m[k],c)
n++
}
for ;n>0; {
r := <-c
ret[r.key]=r.value
n--
}
return(ret)
}
However I was hoping that I would be able to generalize this mapping with the empty interface.
package Mr
type T interface{}
type result struct{
key,value T
}
func wrapper(f func(T) T,k T,v T, c chan result){
c <- result{k,f(v)}
}
func Map(f func(T) T,m map[T]T) map[T]T{
c := make(chan result)
ret := make(map[T]T)
n := 0
for k := range m{
go wrapper(f,k,m[k],c)
n++
}
for ;n>0; {
r := <-c
ret[r.key]=r.value
n--
}
return(ret)
}
Unfortunately when I run main with this generalize Mr.Map I get the following error.
# command-line-arguments
./main.go:22: cannot use f (type func(string) int) as type func(Mr.T) Mr.T in argument to Mr.Map
./main.go:22: cannot use data (type map[int]string) as type map[Mr.T]Mr.T in argument to Mr.Map
So yeah, obviously I understand what the errors are telling me but it seems wild that I would have to re-write my Map function for each possible combination of key and value types.
Is there a work around here, or is this just the nature of the beast?
No real workaround there, that's the nature of the beast.
The language was designed after struggling with C++ for some time, and the idea of the creators was simplifying all non-vital things but at the same time make key additions to make the language more expressive.
You can read a bit about their reasoning here, which I believe is quite interesting even if you don't agree with all the decisions they made:
https://commandcenter.blogspot.com.ar/2012/06/less-is-exponentially-more.html
In your example, if you wanted to, you could make your maps and functions use interface{} (which by the way is called the empty interface and not "nil" interface).
But of course you would lose compile-time type checking and would have to add casts all around.
You can also try to find an interface to express the commonalities of the types you want to use (which might not be so easy or even possible at all), and then build your mapping API around that interface.
The philosophy of Go is not compatible with generalized functions such as is the style with popular dynamic languages. To properly inform the compiler of what you are trying to do, you should express your needed map through an interface or simply by writing it for each type you are using it with.
Mapping requires allocating an array, iterating through a collection, and adding to the array some data element for each element in the collection. If you need a map for a slice of structs, as is common in the application layer, you can express this tersely in Go:
https://play.golang.org/p/pk3Tl_BdlD
Dynamic languages build a "type tree" of "generic" types that allow for terse programming, such as functions like map being called by one symbol over any possible type. This provides a ton of developer productivity because code can be written loosely to allow easy experimentation.
Go is designed for writing semi-permanent software. It performs well because it requires more information to be supplied to the compiler. Map is only about three lines of code, so the cost/benefit of developer productivity v. efficiency lands on the performance side for Go. Functions like map, reduce and filter should be written explicitly as needed.
To evaluate the language, I would encourage you to try to solve a problem with a Go program and see where that takes you.

golang with compile error: undefined: strings in strings.trim

I try to use golang deal with this problem 557. Reverse Words in a String III
my code as below:
import "fmt"
import ss "strings"
func reverseWords(s string) string {
words := ss.Split(s," ");
res := "";
for i:=0; i < len(words);i++{
curWord := ss.Split(words[i],"");
for j:=len(curWord)-1; j >= 0;j--{
res += curWord[j];
}
if(i!=len(words)-1){
res += " ";
}
}
return res;
}
func main(){
s := "Let's take LeetCode contest";
fmt.Println(reverseWords(s));
}
Everything is ok in my pc, it can pass compile at least.
However, when I submit in leetcode it tell me :
Line 67: undefined: strings in strings.Trim
I google this error but get nothing relevant info. As a beginner in golang, I need help. Everything will be appreciated.
You're importing strings under an alias:
import ss "strings"
That means that everywhere in that file, instead of referring to strings you must refer to ss, for example:
words := ss.Split(s," ")
If you use the default import:
import "strings"
Then you can refer to it as strings as normal.
Note that the currently accepted answer is wrong about two things: you can absolutely use the alias as you have it, you just have to refer to the package with the aliased name. It will not cause any issues if you use the name you gave it. Second, you absolutely do need to import the strings package - with or without an alias, your choice - if you want to refer to it.
On a completely unrelated side note, you should strongly consider running go fmt on your code, as it does not follow Go coding standards; for example, standard Go code omits the vast majority of semicolons. The code will work regardless, but you'll have an easier time getting help from other Go developers if your code is formatted the way everyone else is used to seeing it.
If you import strings package with different name then it will cause issue as it is used by the wrapper code to run the function completely.
No need to import strings package again. It will be added.
Just use it directly.
func reverseWords(s string) string {
words := strings.Split(s," ");
res := "";
for i:=0; i < len(words);i++{
curWord := strings.Split(words[i],"");
for j:=len(curWord)-1; j >= 0;j--{
res += curWord[j];
}
if(i!=len(words)-1){
res += " ";
}
}
return res;
}

Evaluate formula in Go

Using Go (golang) I'd like to take a string with a formula and evaluate it with pre-defined values. Here's a way to do it with python's parser module:
x = 8
code = parser.expr("(x + 2) / 10").compile()
print eval(code)
# prints 1
Any idea how to do it with Go?
This package will probably work for your needs: https://github.com/Knetic/govaluate
expression, err := govaluate.NewEvaluableExpression("(x + 2) / 10");
parameters := make(map[string]interface{}, 8)
parameters["x"] = 8;
result, err := expression.Evaluate(parameters);
You will probably need to resort to a library that interprets math statements or have to write your own parser. Python being a dynamic language can parse and execute python code at runtime. Standard Go cannot do that.
If you want to write a parser on your own, the go package will be of help. Example (On play):
import (
"go/ast"
"go/parser"
"go/token"
)
func main() {
fs := token.NewFileSet()
tr, _ := parser.ParseExpr("(3-1) * 5")
ast.Print(fs, tr)
}
The resulting AST (Abstract Syntax Tree) can then be traversed and interpreted as you choose (handling '+' tokens as addition for the now stored values, for example).
I have made my own equation evaluator, using Djikstra's Shunting Yard Algorithm.
It supports all operators, nested parenthesis, functions and even user defined variables.
It is written in pure go
https://github.com/marcmak/calc
go-exprtk package will probably meet all kinds of your needs to evaluate any kind of mathematical expression dynamically.
package main
import (
"fmt"
"github.com/Pramod-Devireddy/go-exprtk"
)
func main() {
exprtkObj := exprtk.NewExprtk()
exprtkObj.SetExpression("(x + 2) / 10")
exprtkObj.AddDoubleVariable("x")
exprtkObj.CompileExpression()
exprtkObj.SetDoubleVariableValue("x", 8)
fmt.Println(exprtkObj.GetEvaluatedValue())
}
This package has many capabilities
There is no such module in Go. You have to build your own. You could use subpackages of the go package, but they might be overkill for your application.
For expression or program evaluation, you can build a lexer and parser using lex and yacc, and specify exactly the syntax and semantics of your mini-language. A calculator has always been a standard yacc example, and the go versions of lex and yacc are no different.
Here's a pointer to the calc example: https://github.com/golang-samples/yacc/tree/master/simple
With this code you can evaluate dynamically any formula and return true or false:
package main
import (
"go/token"
"go/types"
)
func main() {
fs := token.NewFileSet()
tv, err := types.Eval(fs, nil, token.NoPos, "(1 + 4) >= 5")
if err != nil {
panic(err)
}
println(tv.Value.String())
}
There's nothing built in that could do that (remember, Go is not a dynamic language).
However, you can always use bufio.Scanner and build your own parser.
Googling around I found this: https://github.com/sbinet/go-eval
It appears to be an eval loop for Go.
go get github.com/sbinet/go-eval/cmd/go-eval
go install github.com/sbinet/go-eval/cmd/go-eval
go-eval

Resources