How to iterate through an Array Value in PostForm? - go

I've been trying to read the values contained in an array sent by a Post Submit in a website.
Printing the r.PostForm data returns this map:
map[myArray[0].val1:[foo1] myArray[0].val2:[foo2] myArray[1].val1:[foo1] myArray[1].val2:[foo2]]
How can we manipulate the data? I already tried something like this:
func request(r http.ResponseWriter, w *http.Request){
r.ParseForm()
for key, array := range r.PostForm["myArray"] {
// Do something with the val1 and val2 values.
}
}
But this didn't work and I hadn't found any solution in the web.
Is possible to read an array contained in Post data using a basic solution?

It looks like the keys of your form are "myArray[0].val1", "myArray[0].val2", and so on.
Try:
r.ParseForm()
for k, v := range r.PostForm {
for i, value := range v {
// Do something with the i-th value for key k.
}
}

Related

Want to take a map to a slice structure

I have a map with a key and a value structure that i want to put into a simple slice.
The struct I want to store the values as ,
type Attribute struct {
AttId container.AttrID
AttMess []json.RawMessage
}
The current loop I have is something like this which takes the keys from the existing map,
keys := make([]container.AttrID, 0, len(AttId))
for k := range AttId {
keys = append(keys, k)
}
for _, k := range keys {
fmt.Println(k, AttId[k])
}
How would I construct a slice that holds the keys and values inside attributes with the struct above? I'm a bit lost if you can actually make this.
Thanks!
You should simply range over the map and construct instances of the struct. Assuming the map values are []json.RawMessage types:
attrs:=make([]Attribute,0,len(attributes))
for k,v:=range attributes {
attrs=append(attrs,Attribute{AttributesId:k, AttributesMessage:v})
}

Get data from Twitter Library search into a struct in Go

How do I append output from a twitter search to the field Data in the SearchTwitterOutput{} struct.
Thanks!
I am using a twitter library to search twitter base on a query input. The search returns an array of strings(I believe), I am able to fmt.println the data but I need the data as a struct.
type SearchTwitterOutput struct {
Data string
}
func (SearchTwitter) execute(input SearchTwitterInput) (*SearchTwitterOutput, error) {
credentials := Credentials{
AccessToken: input.AccessToken,
AccessTokenSecret: input.AccessTokenSecret,
ConsumerKey: input.ConsumerKey,
ConsumerSecret: input.ConsumerSecret,
}
client, err := GetUserClient(&credentials)
if err != nil {
return nil, err
}
// search through the tweet and returns a
search, _ , err := client.Search.Tweets(&twitter.SearchTweetParams{
Query: input.Text,
})
if err != nil {
println("PANIC")
panic(err.Error())
return &SearchTwitterOutput{}, err
}
for k, v := range search.Statuses {
fmt.Printf("Tweet %d - %s\n", k, v.Text)
}
return &SearchTwitterOutput{
Data: "test", //data is a string for now it can be anything
}, nil
}
//Data field is a string type for now it can be anything
//I use "test" as a placeholder, bc IDK...
Result from fmt.Printf("Tweet %d - %s\n", k, v.Text):
Tweet 0 - You know I had to do it to them! #JennaJulien #Jenna_Marbles #juliensolomita #notjulen Got my first hydroflask ever…
Tweet 1 - RT #brenna_hinshaw: I was in J2 today and watched someone fill their hydroflask with vanilla soft serve... what starts here changes the wor…
Tweet 2 - I miss my hydroflask :(
This is my second week working with go and new to development. Any help would be great.
It doesn't look like the client is just returning you a slice of strings. The range syntax you're using (for k, v := range search.Statuses) returns two values for each iteration, the index in the slice (in this case k), and the object from the slice (in this case v). I don't know the type of search.Statuses - but I know that strings don't have a .Text field or method, which is how you're printing v currently.
To your question:
Is there any particular reason to return just a single struct with a Data field rather than directly returning the output of the twitter client?
Your function signature could look like this instead:
func (SearchTwitter) execute(input SearchTwitterInput) ([]<client response struct>, error)
And then you could operate on the text in those objects in wherever this function was called.
If you're dead-set on placing the data in your own struct, you could return a slice of them ([]*SearchTwitterOutput), in which case you could build a single SearchTwitterOutput in the for loop you're currently printing the tweets in and append it to the output list. That might look like this:
var output []*SearchTwitterOutput
for k, v := range search.Statuses {
fmt.Printf("Tweet %d - %s\n", k, v.Text)
output = append(output, &SearchTwitterOutput{
Data: v.Text,
})
}
return output, nil
But if your goal really is to return all of the results concatenated together and placed inside a single struct, I would suggest building a slice of strings (containing the text you want), and then joining them with the delimiter of your choosing. Then you could place the single output string in your return object, which might look something like this:
var outputStrings []string
for k, v := range search.Statuses {
fmt.Printf("Tweet %d - %s\n", k, v.Text)
outputStrings = append(outputStrings, v.Text)
}
output = strings.Join(outputStrings, ",")
return &SearchTwitterOutput{
Data: output,
}, nil
Though I would caution, it might be tricky to find a delimiter that will never show up in a tweet..

Updating a key in a map, whilst iterating over that map

I want to update the key from one name to another using URL params. I have the code, but the output is incorrect. See below.
This is the map
var data map[string][]string
The PUT method for the function im calling
r.HandleFunc("/updatekey/{key}/{newkey}", handleUpdateKey).Methods("PUT")
The handleUpdateKey func, which is noted up explaining exactly what it's doing.
func handleUpdateKey(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
k := params["key"] //get url params
nk := params["newkey"]
s := make([]string, len(data[k])) //create slice of string to store map variables
for i := range data { //range over the data map
fmt.Fprintf(w, i)
if k != i { //check if no keys exist with URL key param
fmt.Fprintf(w, "That KEY doesn't exist in memory")
break //kill the loop
} else { //if there is a key the same as the key param
for _, values := range data[i] { //loop over the slice of string (values in that KEY)
s = append(s, values) //append all those items to the slice of string
}
delete(data, k) //delete the old key
for _, svalues := range s { //loop over the slice of string we created earlier
data[nk] = append(data[nk], svalues) //append the items within the slice of string, to the new key... replicating the old key, with a new key name
}
}
}
}
The below should assign all the values of that KEY to a slice of string, which we later iterate over and add to the new KEY. This works, however, the output is as below which is clearly incorrect
KEY: catt: VALUE:
KEY: catt: VALUE:
KEY: catt: VALUE: zeus
KEY: catt: VALUE: xena
OLD OUTPUT:
KEY: dog: VALUE: zeus
KEY: dog: VALUE: xena
CORRECT NEW OUTPUT:
KEY: catt: VALUE: zeus
KEY: catt: VALUE: xena
In most languages, altering a structure you're iterating over will cause strange things to happen. Particularly maps. You have to find another way.
Fortunately there's no need to iterate at all. Your loop is just one big if/else statement. If the key matches, do something. If it doesn't, do something else. Since this is a map, there's no need to search for the key using iteration, it can be looked up directly. There's also no need for all that laborious looping just to copy a map value.
if val, ok := data[k]; ok {
// Copy the value
data[nk] = val
// Delete the old key
delete(data, k)
} else {
fmt.Fprintf(w, "The key %v doesn't exist", k)
}
Finally, avoid using globals in functions. It makes it difficult to understand what effect a function has on the program if it can change globals. data should be passed in to the function to make it clear.
func handleUpdateKey(w http.ResponseWriter, r *http.Request, data map[string][]string)

go map search using values in a map

I have a map structured like map[string][]string. Now I have to find all keys which have the required values in the value slice. I can do something like this:
// allsvc is map[string][]string
var newsl []string
for k, v := range allsvc {
for _, val := range v {
if v == "type1" || v == "type2" {
newsl.append(k)
}
}
}
The map allsvc has atleast a half million entries at any given time, and the lookup is quite frequent. I get the allsvc map as an ouput of a 3rd party library and then I have to search in it using values in my api and provide a response. Given the high frequency of lookup not using keys but with values, the way i have done it makes my api response time to be in the seconds. Is there a way to better performance (speed of lookup)?
If you will query that map multiple times, it might be worth spending some time re-arranging it when you get it so that you can then query it faster.
It seems you need to invert the relationships, making the values in allsvc the keys in the new map, and having the keys as values so that you can then just make lookups in the new map.
This can be a way to re-arrange the map:
func arrangeMap(oldMap map[string][]string) map[string][]string {
newMap := make(map[string][]string)
for k, v := range oldMap {
for _, val := range v {
newMap[val] = append(newMap[val], k)
}
}
return newMap
}
See here a playground showing the idea:
https://play.golang.org/p/0ThZlX9xUn

r.ParseForm field order

So I would like to preserve the order of the post fields.
But now using the http ParseForm function it will put the fields into a map which will have a different order each time.
The original query : a=1&b=2&c=3 can become b=2&c=3&a=1 or any random order.
Since I hash the query and compare it with the hash of the user his query the hash on my side changes all the time since the order of the fields are random.
Code:
func parsePostQuery(r *http.Request, hashQuery string) bool {
urlquery := url.Values{}
r.ParseForm()
for k, p := range r.Form {
urlquery.Set(k, p[0])
}
//some psuedo code
if hashQuery == hash(urlquery.Encode()){
return true
}
return false
}
How can I parse the fields that are submitted by the user and keep the field order of the user?
Sidenote: I do not know the field names in advance.
In the same area as other answers, you will need the clients to calculate their hash by alphabetizing all parameters before hashing. The code you've supplied should work fine; values.Encode() will sort the values by key on it's own:
Encode encodes the values into “URL encoded” form ("bar=baz&foo=quux") sorted by key.
There's no way to preserve the ordering of the client; in fact, what you receive may not even be how it was ordered on the client end. However unlikely, there's no guarantee that intermediate processes won't change things.
tl;dr: You can't implicitly. The underlying data structure is a map, for which the order is not guaranteed. You need to take additional steps.
However Go maps in action shows an easy way to access the map in a sorted way. You create a slice of the keys, sort that slice and access the map value by iterating over the keys in the sorted slice.
For your example, it would look something like this
package main
import (
"crypto/md5"
"fmt"
"io"
"sort"
)
func main() {
// Which is the same structure as url.Values()
var m map[string][]string = make(map[string][]string)
m["c"] = []string{"19.95"}
m["b"] = []string{"foo", "bar", "baz"}
m["a"] = []string{"1"}
// Note that playground is deterministic, so the order should be preserved there
// However, you can not rely on that in the real world
fmt.Println("Unsorted")
for k, v := range m {
fmt.Println("Key:", k, "Value:", v)
}
var keys []string
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
hash := md5.New()
fmt.Println("\nSorted")
for _, k := range keys {
fmt.Println("Key:", k, "Value:", m[k])
// Add Key/Value pair to hash
fmt.Printf("\tAdding KEY '%s' to hash\n", k)
io.WriteString(hash, k)
for _, v := range m[k] {
fmt.Printf("\tAdding VALUE '%s' to hash\n", v)
io.WriteString(hash, v)
}
}
fmt.Printf("\nHash: %x", hash.Sum(nil))
}
Run above code on Playground
You can read the request body and check for the form parameters. They will appear in the same order as in the request(hope your client application is also aware of this order preserving)
You can create a reader for reading the request body. A sample code looks like
body, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Println("Reading Error ")
return
}
fmt.Println("Req Body : ", string(body))
Note : Be aware of the content type

Resources