Am I using redigo HDEL correctly? - go

I seem to have the correct usage for using the HDEL command interface, but seem to get 0 records deleted. Am I missing something here?
Here are useful code snippets:
This doesn't work:
keysToDeleteArr []string //this has valid key values from upstream
s.transClient.Do("MULTI")
_, err := s.transClient.Do("HDEL", myhash, keysToDeleteArr)
s.transClient.Do("EXEC")
Gives an output (int64) 0 // # of keys deleted
This does work:
s.transClient.Do("MULTI")
for _, keyToDelete := range keysToDeleteArr {
_, err := s.transClient.Do("HDEL", myhash, keyToDelete)
}
s.transClient.Do("EXEC")
Gives an output (int64) 1 for each HDEL. From the documentation and from static code analysis on the redigo lib, does seem like slices are acceptable parameters for fields

Construct an []interface{} containing the command arguments. Pass the slice of interface{} to the Do method as a variadic argument:
args := make([]interface{}, 0, len(keysToDeleteArr) + 1)
args = append(args, myhash)
for _, v := range keysToDeleteArr {
args = append(args, v)
}
_, err := s.transClient.Do("HDEL", args...)
Use Redigo's Args to execute the code above in a single line:
_, err := s.transClient.Do("HDEL", redis.Args{}.Add(myhash).AddFlat(keysToDeleteArr)...)

Related

Convert string response to map in golang

I'm using https://godoc.org/github.com/andygrunwald/go-jira#IssueService.GetCustomFields to get a custom field and I'm attempting to consume some of the data.
func getsomedata(issue_id string) {
issue, _, _ := jiraClient.Issue.Get(issue_id, nil)
fields, _, _ := jiraClient.Issue.GetCustomFields(issue_id)
data := fields["customfield_123456"]
}
Something similar to the following (unformatted) is returned as a single string, how can I convert this back into a struct or map? The end goal is to store "key.value" and "name.value"
[
map[
key:key.value
name:name.value
]
map[
key:key.value
name:name.value
]
]
I figured it out, type assertion is the way to solve this.
func getsomedata(issue_id string) {
issue, _, _ := jiraClient.Issue.Get(issue_id, nil)
// This returns as a slice of interfaces []interfaces{}
data := issue.Fields.Unknowns["customfield_12345"]
// Will use the length of the slice in the for loop below
s := reflect.ValueOf(data)
// For each index index in the slice, do...
for i := 0; i < s.Len(); i++ {
// Use type assertion to get the value I need for all indexes "i"
d := data.([]interface{})[i].(map[string]interface{})
fmt.Printf("%v\n", d["key"].(string))
}
}

Passing []*io.PipeWriter to io.MultiWriter

I have a bunch of *io.PipeWriter created and would like to make a multiwriter based on all those pipewriters in a function. So I call a function something like
func copyToWriters(reader *bufio.Reader, errs chan error, writers []*io.PipeWriter) {
for _, writer := range writers {
defer writer.Close()
}
mw := io.MultiWriter(writers)
_, err := io.Copy(mw, reader)
if err != nil {
errs <- err
}
}
I call the method with arguments copyToWriters(reader, errs, []*io.PipeWriter{w1, w2})
But it says
cannot use writers (type []*io.PipeWriter) as type []io.Writer in argument to io.MultiWriter. But
if I change io.MultiWriter(writers) to io.MultiWriter(writers[0],writers[1]) it works. How can I make the existing function work by not having to pass writers separately.
Unfortunately, Golang's type system does not allow casting []*io.PipeWriter to []io.Writer even when *io.PipeWriter implements io.Writer since it requires O(n) operation (reference).
The best you can do is create another []io.Writer and copy the pipe writers into it
ws := make([]io.Writer, len(writers))
for i, w := range writers {
ws[i] = w
}
mw := io.MultiWriter(ws...)
And reason why you nead ..., read the document

GEOADD command with Redigo

Here's what i'm trying, using Redigo ("github.com/garyburd/redigo/redis") :
insertPos := []string{"3.361389", "38.115556", "12"}
if _, err := conn.Do("GEOADD", redis.Args{}.Add("geoIndex").AddFlat(&insertPos)...); err != nil {
log.Print(err)
}
==> "ERR wrong number of arguments for 'geoadd' command"
While with the redis-cli this works fine :
GEOADD geoIndex 3.361389 38.115556 12
==> (integer) 1
Other commands works fine, that's just the first time I've to use GEOADD and it clearly doesn't seem to work as I expect it to.
Does someone have an idea ?
The easiest way to call this API is:
_, err := conn.Do("GEOADD", "geoIndex", "3.361389", "38.115556", "12")
You can also pass number values:
_, err := conn.Do("GEOADD", "geoIndex", 3.361389, 38.115556, 12)
If you do want to piece the command together, then pass the slice to AddFlat, not a pointer to the slice:
insertPos := []string{"3.361389", "38.115556", "12"}
_, err := conn.Do("GEOADD", redis.Args{}.Add("geoIndex").AddFlat(insertPos)...)

Write a slice of any type to a file in Go

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

How to combine multiple assignment and Range in for loops

I'm trying to figure out how to (or if it's possible to) combine multiple assignment and ranges in Golang
ex pseudo code of what I'd like to do
files := [2]*os.File{}
for i, _, fileName := 0, range os.Args[1:3] {
files[i], _ = os.Open(fileName)
}
The idea being I want to have both an iteration counter (i) and the filenames (fileName). I know this can be achieved by using the key from range and some math (key -1), thats not the point of the example.
Edit:
Upon debugging the above example, I learned that i will range 0-1 in that example; Because os.Args[1:2] is a slice and that slice has indexing 0-1 . Therefore I dont need "some math" to properly index the keys.
** EDIT 2: **
This post is also a must read as to why the above [2]*os.File{} is not idiomatic go, instead it should not have a size specified (files := []*os.File{}) so that files is of type slice of *os.File
There are a lot of different issues here. First, range already does what you want. There's no need for even math.
for i, fileName := range os.Args[1:] {
i will range from 0 to 1 here, just like you want. Ranging over a slice always starts at index 0 (it's relative to the start of the slice). (http://play.golang.org/p/qlVM6Y7yPD)
Note that os.Args[1:2] is just one element. You probably meant it to be two.
In any case, this is likely what you really meant:
http://play.golang.org/p/G4yfkKrEe7
files := make([]*os.File, 0)
for _, fileName := range os.Args[1:] {
f, err := os.Open(fileName)
if err != nil {
log.Fatalf("Could not open file: %v", err)
}
files = append(files, f)
}
fmt.Printf("%v\n", files)
Fixed-length arrays are very uncommon in Go. Generally you want a slice, created with make.
For example,
so.go:
package main
import (
"fmt"
"os"
)
func main() {
files := [2]*os.File{}
for i, fileName := range os.Args[1:] {
if i >= len(files) {
break
}
var err error
files[i], err = os.Open(fileName)
if err != nil {
// handle error
}
}
fmt.Println(files)
}
Output:
$ go build so.go && ./so no.go so.go to.go
[<nil> 0xc820030020]
$

Resources