I am trying to figure out why my code is not working. I wish to take a slice of numbers and strings, and separate it into three slices. For each element in the slice, if it is a string, append it to the strings slice, and if it is a positive number, append it to the positive numbers, and likewise with negative. Yet, here is the output
Names:
EvTremblay
45.39934611083154
-75.71148292845268
[Crestview -75.73795670904249
BellevueManor -75.73886856878032
Dutchie'sHole -75.66809864107668 ...
Positives:[45.344387632924054 45.37223315413918 ... ]
Negatives: []
Here is my code. Can someone tell me what is causing the Negatives array to not have any values?
func main() {
fmt.Printf("%q\n", strings.Split("a,b,c", ","))
var names []string
var positives, negatives []float64
bs, err := ioutil.ReadFile("poolss.txt")
if err != nil {
return
}
str := string(bs)
fmt.Println(str)
tokens := strings.Split(str, ",")
for _, token := range tokens {
if num, err := strconv.ParseFloat(token, 64); err == nil {
if num > 0 {
positives = append(positives, num)
} else {
negatives = append(negatives, num)
}
} else {
names = append(names, token)
}
fmt.Println(token)
}
fmt.Println(fmt.Sprintf("Strings: %v",names))
fmt.Println(fmt.Sprintf("Positives: %v", positives))
fmt.Println(fmt.Sprintf("Negatives: %v",negatives))
for i := range names{
fmt.Println(names[i])
fmt.Println(positives[i])
fmt.Println(negatives[i])
}
}
Your code has strings as a variable name:
var strings []string
and strings as a package name:
tokens := strings.Split(str, ",")
Don't do that!
strings.Split undefined (type []string has no field or method Split)
Playground: https://play.golang.org/p/HfZGj0jOT-P
Your problem above I think lies with the extra \n attached to each float probably - you get no negative entries if you end in a linefeed or you would get one if you have no linefeed at the end. So insert a printf so that you can see the errors you're getting from strconv.ParseFloat and all will become clear.
Some small points which may help:
Check errors, and don't depend on an error to be of only one type (this is what is confusing you here) - always print the error if it arrives, particularly when debugging
Don't use the name of a package for a variable (strings), it won't end well
Use a datastructure which reflects your data
Use the CSV package to read CSV data
So for example for storing the data you might want:
type Place struct {
Name string
Latitude int64
Longitude int64
}
Then read the data into that, depending on the fact that cols are in a given order, and store it in a []Place.
Here's what I tried, it works now! Thanks for the help, everyone!
func main() {
findRoute("poolss.csv", 5)
}
func findRoute( filename string, num int) []Edge {
var route []Edge
csvFile, err := os.Open(filename)
if err != nil {
return route
}
reader := csv.NewReader(bufio.NewReader(csvFile))
var pools []Pool
for {
line, error := reader.Read()
if error == io.EOF {
break
} else if error != nil {
log.Fatal(error)
}
lat, err := strconv.ParseFloat(line[1], 64)
long, err := strconv.ParseFloat(line[2], 64)
if err == nil {
pools = append(pools, Pool{
name: line[0],
latitude: lat,
longitude: long,
})
}
}
return route
}
Related
I have a Go container that calculates the total marks scored, and works perfectly.
For this to be able to be read by my proxy file, i need the return value to be json.
I am trying this however it isnt working:
// Find the total grade
marksSum := 0
for _, mark := range marks {
marksSum += mark
}
j, _ := json.Marshal(markSum)
return j
Any help is much appreciated!
You can create a struct of how you want to structure your JSON object.(variable names should start with a capital letters)
type Response struct {
Error error `json:"error"`
Input_text string `json:"string"`
Answer int `json:"answer"`
}
Then just create a response using the above struct.
func main() {
marks := []int{1, 2, 3, 4}
marksSum := 0
input := ""
for _, mark := range marks {
input = fmt.Sprintf("%s %d", input, mark)
marksSum += mark
}
resp := &Response{
Error: nil,
Input_text: input,
Answer: marksSum,
}
j, err := json.Marshal(resp)
if err != nil {
fmt.Printf("Errr : %v", err)
return
}
fmt.Println(string(j))
}
https://go.dev/play/p/iC484GS7GKS
is there a way to scan only integers in Go? like an age or a number of order etc. so you don't want user to enter letters or signs.
Scanf apparently don't work, it get skipped and print instead "unexpected newline"
func AgeInput(age int) {
fmt.Println("enter your Age :..")
_, err := fmt.Scanf("%d", &UserAge)
if err != nil {
fmt.Println(err)
}
}
I also tried to use contains and containsAny to check if the input has any numbers and if not then it is not valid and the user will be asked to try again but it returns either always true or always false
func ValidateAge(age int){
AgeInput(age)
if strings.Contains(strconv.Itoa(UserAge), "1234567890"){
validAge = true
}
for !validAge {
fmt.Println("wrong input try again")
AgeInput(age)
}
}
I think you were on the right track here, but just need to tweak you code a little:
package main
import (
"fmt"
)
func main() {
fmt.Println("Enter your Age: ")
var userAge int
for true {
_, err := fmt.Scanf("%d", &userAge)
if err == nil {
break
}
fmt.Println("Not a valid age - try again")
var dump string
fmt.Scanln(&dump)
}
fmt.Println(userAge)
}
Scanf only scans until it gets a match, but will leave the rest of the line untouched, so we need to clear the STDIN buffer with a fmt.Scanln
I'm doing book gopl's exercise 1.12, Basically, the code need to update several default values from http.Request if it is present in URL parameters.
Say here is the code I'm working on:
var ( // Need to update those values if corresponding parameter present in URL
cycles = 5
res = 0.001
size = 100
)
I can do the updating one by one:
if c := r.FormValue("cycles"); c != "" { // r is a *http.Request
i, err := strconv.ParseInt(c); err != nil {
cycles = i
}
}
if r := r.FormValue("res"); r != "" {
if f, err := strconv.ParseFloat(r); err != nil {
res = f
}
}
// ...
But I'm not satisfied by this solution:
If I have dozens of params, this is very cumbersome
How to handle the conversion errors?
The repeating pattern seems requires a function, like this (I don't know how to implement it yet, just showing my thought)
func setParam(p interface{}, name string, r *http.Request) error {
if f := r.FormValue(name); f != "" {
switch p.(type) {
case int:
// strconv.ParseInt
case float64:
// strconv.PraseFloat
// ...
}
}
This looks better, but still cumbersome. I don't know if this is the best solution. Or I overlooked some feature in Go that should be used in this situation.
So, what's the idiomatic way to do this?
Write functions that get a form value as a specific type or return default when value is missing. Example:
func intValue(r *http.Request, name string, def int) (int, error) {
if _, ok := r.Form[name]; !ok {
return def, nil
}
return strconv.Atoi(r.FormValue(name))
}
Call these functions from your handler. This is repetitive like the code in the question, but combines variable declaration, default value and fetching value in a single line of code.
cycles, err := intValue(r, "cycles", 5)
if err != nil {
// TODO; handle bad value
}
Let's say I have a bunch of array of strings, for example:
data := [4]string{"a", "3.0", "2.5", "10.7"}
And a struct definition:
type Record struct {
name string
x float64
y float64
mag float64
}
I'd like to create an instance of this struct from each array.
I need to match the first item of the array to the first field of the struct and so on. Is it possible to do this?
Each array corresponds to one line of a file, so I can actually decide how to read these values in case a different approach is better.
An easy way is to use reflection to iterate over the fields of the struct, obtain their address (pointer), and use fmt.Sscan() to scan the string value into the field. fmt.Sscan() will handle the different types of fields for you. This is in no way an efficient solution, it is just a short, easy and flexible solution. If you need an efficient solution, you have to handle all fields explicitly, manually.
This only works if the fields of the struct are exported, e.g.:
type Record struct {
Name string
X float64
Y float64
Mag float64
Age int
}
The function that loads a string slice into a struct value:
func assign(recordPtr interface{}, data []string) error {
v := reflect.ValueOf(recordPtr).Elem()
max := v.NumField()
if max > len(data) {
max = len(data)
}
for i := 0; i < max; i++ {
if _, err := fmt.Sscan(data[i], v.Field(i).Addr().Interface()); err != nil {
return err
}
}
return nil
}
Note that this implementation tries to fill as many fields as possible (e.g. it does not return an error if the struct has more or less fields than input data provided). Also note that this assign() function can fill any other structs, not just Record, that's why it's flexible.
Example testing it:
data := []string{"a", "3.0", "2.5", "10.7", "23"}
var r Record
if err := assign(&r, data); err != nil {
fmt.Println("error:", err)
}
fmt.Printf("%+v", r)
Output (try it on the Go Playground):
{Name:a X:3 Y:2.5 Mag:10.7 Age:23}
There is no simple way to do this. You have to assign struct members one by one.
for _, x := range data {
x, err := strconv.ParseFloat(x[1])
y, err := strconv.ParseFloat(x[2])
max, err := strconv.ParseFloat(x[3])
strData = append(strData, Record{name: x[0], x: x, y: y, mag: mag})
}
You also have to deal with parse errors.
Of course it it possible to do that in Go.
The following code example will assign to s the record filled with the fields of data.
package main
import (
"fmt"
"strconv"
)
type Record struct {
name string
x float64
y float64
mag float64
}
func main() {
data := [4]string{"a", "3.0", "2.5", "10.7"}
x, err := strconv.ParseFloat(data[1], 64)
if err != nil {
panic(err)
}
y, err := strconv.ParseFloat(data[2], 64)
if err != nil {
panic(err)
}
mag, err := strconv.ParseFloat(data[3], 64)
if err != nil {
panic(err)
}
s := Record{ name: data[0], x: x, y: y, mag: mag}
fmt.Println(s)
}
For logging purposes I want to be able to quickly write a slice of any type, whether it be ints, strings, or custom structs, to a file in Go. For instance, in C#, I can do the following in 1 line:
File.WriteAllLines(filePath, myCustomTypeList.Select(x => x.ToString());
How would I go about doing this in Go? The structs implement the Stringer interface.
Edit: I in particular would like the output to be printed to a file and one line per item in the slice
Use the fmt package format values as strings and print to a file:
func printLines(filePath string, values []interface{}) error {
f, err := os.Create(filePath)
if err != nil {
return err
}
defer f.Close()
for _, value := range values {
fmt.Fprintln(f, value) // print values to f, one per line
}
return nil
}
fmt.Fprintln will call Stringer() on your struct type. It will also print int values and string values.
playground example
Use the reflect package to write any slice type:
func printLines(filePath string, values interface{}) error {
f, err := os.Create(filePath)
if err != nil {
return err
}
defer f.Close()
rv := reflect.ValueOf(values)
if rv.Kind() != reflect.Slice {
return errors.New("Not a slice")
}
for i := 0; i < rv.Len(); i++ {
fmt.Fprintln(f, rv.Index(i).Interface())
}
return nil
}
If you have variable values of type myCustomList, then you can call it like this: err := printLines(filePath, values)
playground example