Convert string in struct to []string - go

I have a struct as below:
type TourData struct {
ArtistID int //artist ID
RelationID string //key for relations
City string
Country string
TourDates []string
}
type MyRelation struct {
ID int `json:"id"`
DatesLocations map[string][]string `json:"datesLocations"`
}
which contains this data from a csv file:
1,nagoya-japan,Nagoya,Japan,
1,penrose-new_zealand,Penrose,New_Zealand,
1,dunedin-new_zealand,Dunedin,New_Zealand,
2,playa_del_carmen-mexico,Playa Del Carmen,Mexico,
2,papeete-french_polynesia,Papeete,French_Polynesia,
MyRelations is populated from an API which contains:
"index": [
{
"id": 1,
"datesLocations": {
"dunedin-new_zealand": [
"10-02-2020"
],
"nagoya-japan": [
"30-01-2019"
],
"penrose-new_zealand": [
"07-02-2020"
]
}
},
{
"id": 2,
"datesLocations": {
"papeete-french_polynesia": [
"16-11-2019"
],
"playa_del_carmen-mexico": [
"05-12-2019",
"06-12-2019",
"07-12-2019",
"08-12-2019",
"09-12-2019"
]
}
}
The dates come from another struct. The code I have used to populate this struct is as below:
var oneRecord TourData
var allRecords []TourData
for _, each := range csvData {
oneRecord.ArtistID, _ = strconv.Atoi(each[0])
oneRecord.RelationID = each[1]
oneRecord.City = each[2]
oneRecord.Country = each[3]
oneRecord.TourDates = Relations.Index[oneRecord.ArtistID-1].DatesLocations[each[1]]
allRecords = append(allRecords, oneRecord)
}
jsondata, err := json.Marshal(allRecords) // convert to JSON
json.Unmarshal(jsondata, &TourThings)
I need to group all the 1s together then the 2s etc. I thought to create another struct, and populate from this one but not having much luck - any ideas?
To clarify I would want say TourData.City to equal:
[Nagoya,Penrose,Dunedin]
[Playa Del Carmen, Papeete]
At the moment if I was to print TourData[0].City I would get Nagoya.
I have tried creating another struct to be populated from the TourData struct with the following fields:
type TourDataArrays struct {
ArtistID int
City []string
Country []string
TourDates [][]string
}
and then populate the struct using the code below:
var tourRecord TourDataArrays
var tourRecords []TourDataArrays
for i := 0; i < len(Relations.Index); i++ {
for j := 0; j < len(allRecords); j++ {
if allRecords[i].ArtistID == i+1 {
tourRecord.City = append(tourRecord.City, allRecords[j].City)
}
}
tourRecords = append(tourRecords, tourRecord)
}
However this is adding all the cities to one array i.e
[Nagoya, Penrose, Dunedin, Playa Del Carmen, Papeete].

If I understand your requirements correctly you needed to declare city as a string array as well. (And Country to go with it).
Check out this solution : https://go.dev/play/p/osgkbfWV3c5
Note I have not deduped country and derived city and country from one field in the Json.

Related

How to pass struct as a function argument in Go?

package main
import "fmt"
type Person struct {
name string
age int
job string
salary int
}
func test(class Person) {
// Access and print Pers1 info
fmt.Println("Name: ", class.name)
fmt.Println("Age: ", class.age)
fmt.Println("Job: ", class.job)
fmt.Println("Salary: ", class.salary)
}
func main() {
var pers1 Person
var pers2 Person
// Pers1 specification
pers1.name = "Hege"
pers1.age = 45
pers1.job = "Teacher"
pers1.salary = 6000
// Pers2 specification
pers2.name = "Cecilie"
pers2.age = 24
pers2.job = "Marketing"
pers2.salary = 4500
}
/* This is my code. I want to pass whole struct to a function test as argument. But i don't know the syntax of like how can i achieve this. Kindly look into this and help me*/
You should pass it to function calling as test(pers1) and test(pers2)
package main
import "fmt"
type Person struct {
name string
age int
job string
salary int
}
func test(class Person) {
// Access and print Pers1 info
fmt.Println("Name: ", class.name)
fmt.Println("Age: ", class.age)
fmt.Println("Job: ", class.job)
fmt.Println("Salary: ", class.salary)
}
func main() {
var pers1 Person
var pers2 Person
// Pers1 specification
pers1.name = "Hege"
pers1.age = 45
pers1.job = "Teacher"
pers1.salary = 6000
// Pers2 specification
test(pers1)
pers2.name = "Cecilie"
pers2.age = 24
pers2.job = "Marketing"
pers2.salary = 4500
test(pers2)
}
/* Try this you have to pass it as test(pers1) and test(pers2). I hope it works fine now.*/

Creating grpc client request with repeated fields

I have proto file like this:
message StartAssignmentRequest {
string additional_comment = 3;
repeated RideSlip slips = 4;
}
message RideSlip{
string slip_name = 2;
string slip_ext = 3;
string slip_link = 4;
}
Now I want to create its request and I am doing something like this:
req := &api.StartAssignmentRequest{
AdditionalComment:"AdditionalComment",
Slips: &api.RideSlip[],
}
but not having idea how I can send RideSlip data properly.
Protobuffer (both 2 and 3) repeated fields are compiled to slices in Go.
Just append to it:
req := &api.StartAssignmentRequest{
AdditionalComment: "AdditionalComment",
}
req.Slips = append(req.Slips, &api.RideSlip{
SlipName: "foo",
SlipExt: "bar",
SlipLink: "https://stackoverflow.com",
})
Or assign to it a literal value:
req := &api.StartAssignmentRequest{
AdditionalComment: "AdditionalComment",
Slips: []*api.RideSlip{
{
SlipName: "foo",
SlipExt: "bar",
SlipLink: "https://stackoverflow.com",
},
},
}

How to edit an array that is buried in a recursive struct

I have this struct (notice that it is recursive!):
type Group struct {
Name string
Item []string
Groups []Group
}
And I want to append a string to the Item array that is buried deep in the hierarchy of the Group array. The only information I have about the path of this new item is the names of the groups that it's in. Let's say the path is "foo/bar/far". I want to modify bar without overwriting foo, bar or the "root" array. Basically, I want to write a function that returns a new Group variable that is identical to the original variable but with the new string appended.
So far I've tried the following method:
Looping through an array that contains all the group names of the path and if they are in the current group set a current group variable to that new group. Once the loop has finished, append the string to the array and return current group. The only problem is, of course, that the rest of the root group is deleted and replaced with the new, modified group.
The code:
func in(item string, array []Group) (bool, int) {
for i, elem := range array {
if item == elem.Name {
return true, i
} else {
continue
}
}
return false, 0
}
func addItem(list Group, newItem string, path string) Group {
var currentGroup Group = list
if path == "" {
currentGroup.Items = append(currentGroup.Items, newItem)
} else {
for _, elem := range strings.Split(path, "/") {
in, index := in(elem, currentGroup.Groups)
if in {
currentGroup = currentGroup.Groups[index]
}
}
currentGroup.Items = append(currentGroup.Items, newItem)
}
return currentGroup
}
I guess you could pass the group to addItem function as a pointer, and ignore the return value for the function
A bit like
func addItem(list *Group, newItem string, path string) Group {
var currentGroup *Group = list
if path == "" {
currentGroup.Item = append(currentGroup.Item, newItem)
} else {
for _, elem := range strings.Split(path, "/") {
in, index := in(elem, currentGroup.Groups)
if in {
currentGroup = &currentGroup.Groups[index]
}
}
currentGroup.Item = append(currentGroup.Item, newItem)
}
return *currentGroup
}
Complete example at:
https://play.golang.org/p/_1BSF2LDQrE

Assign value to field in struct if empty

I have a struct defined
type data struct {
invitecode string
fname string
lname string
}
which I populate from retrieving form data after parsing
...
r.ParseForm()
new user := &data{
invitecode: r.FormValue("invitecode"),
fname: r.FormValue("fname")
lname: r.FormValue("lname")
}
I did like to check if the invitecode field obtained from the form is empty and if so, populate it by calling a function but if it is not, to populate it with the retrieved value (invitecode: if newUser.invitecode == "" {"Mr"} else {lnames.title},). I understand go doesn't have a tenary operator which I thought of using and reading the questions here, here, here & here implies using an if else statement but I can't seem to get it to work. Preferable, I am looking for a solution that check's while assigning a new variable. Trying the code below doesn't seem to work. Any help would be appreciated.
package main
import (
"fmt"
)
type data struct {
invitecode string
fname string
lname string
}
func main() {
var user data
newUser := map[string]string{"invitecode": "", "fname": "Dude", "lname": "Did"}
user = &data{
invitecode: if newUser.invitecode == "" {"Mr"} else {lnames.title},
fname: newUser.fname,
lname: newUser.lname,
}
fmt.Println(user)
}
You cannot use an if ... else statement inline like you would a ternary operator (or if/else statements) in other languages, you must simply do it procedurally:
user := &data{ /* ... */ }
if user.invitecode == "" {
user.invitecode = "Mr"
} else {
user.invitecode = lnames.title
}
Go does not have ternaries, nor can you do an inline if like you've shown in the code. You will have to do a normal if block:
user = &data{}
if newUser.inviteCode == "" {
user.invitecode = "Mr"
} else {
user.invitecode = lnames.title
}
And so on. You could extract this into a function:
func coalesce(args ...string) string {
for _,str := range args {
if str != "" {
return str
}
}
return ""
}
And use it like so:
user.invitecode = coalesce(lnames.title, "Mr")
Of course, if you deal with multiple types (not just strings), you'll need one such function for each type.

Filtering multiple times on one dictionary

I currently run this code:
searchterm = "test"
results = resultsArray.filter { $0.description.contains (searchterm!) }
My question is how do I search in company_name or place or any other field in my model and add it to the results.
Do I need to use filters together and then append the results to a new variable instance of my model?
EDIT:
If "test" is in company_name, place and description. I want all three results returned. However, if "test" is only in place, I need only place to be returned.
EDIT2:
This is an example of my model return. Is this a dictionary or an array? I'm sorry I dont 100% percent know the difference. I know ' "this": is ' what a dictionary looks like, however because there were [] brackets around them, I thought that made it an array...
struct GraphData {
var description: String
var company_name: String
var places: String
init(description: String, company_name: String, places: String){
self.description = description
self.company_name = company_name
self.places = places
}
func toAnyObject() -> Any {
print("return")
return [
"description": description,
"company_name": company_name,
"places": places,
]
}
The easiest way to do this would be to create a custom contains method in your model which can you can use to match the search term against any property in the model:
class YourModel {
var company_name: String
var description: String
var place: String
// ...
func contains(_ searchTerm: String) -> Bool {
return self.company_name.contains(searchTerm)
|| self.description.contains(searchTerm)
|| self.place.contains(searchTerm)
}
}
You can then simply filter using your custom method:
let searchTerm = "test"
let results = resultsArray.filter { $0.contains(searchTerm) }
Is this resultsArray a dictionary or an array?
You can do something like this
let searchTerm = "test"
let filter = resultsArray.filter{ $0.company_name!.contains(searchTerm) || $0.place!.contains(searchTerm) }
Edit
class TestClass: NSObject {
var place: String?
var company_name: String?
func contain(searchTerm: String) -> [String] {
var result = [String]()
if let placeVal = place, placeVal.contains(searchTerm) {
result.append(placeVal)
}
if let companyVal = company_name, companyVal.contains(searchTerm) {
result.append(companyVal)
}
return result
}
}
let searchTerm = "test"
let filter = resultsArray.map { $0.contain(searchTerm: searchTerm) }

Resources