How to remove duplicates in an interface array - go

Lets say the input can contain string or integer values
names = ["rahul", "rohit","srujan", "rahul"] --> output = ["rahul", "rohit","srujan"]
age=[12,18,12,21] --> output = [12,18,21]
we can make use of this function to filter duplicates
package main
import (
"fmt"
)
func unique(intSlice []int) []int {
keys := make(map[int]bool)
list := []int{}
for _, entry := range intSlice {
if _, value := keys[entry]; !value {
keys[entry] = true
list = append(list, entry)
}
}
return list
}
func main() {
intSlice := []int{1,5,3,6,9,9,4,2,3,1,5}
fmt.Println(intSlice)
uniqueSlice := unique(intSlice)
fmt.Println(uniqueSlice)
}
This works only if the input is either string or integer but not both
How to make sure this function works for array interface

Use the reflect package to write a function that works with any slice type:
func unique(src interface{}) interface{} {
srcv := reflect.ValueOf(src)
dstv := reflect.MakeSlice(srcv.Type(), 0, 0)
visited := make(map[interface{}]struct{})
for i := 0; i < srcv.Len(); i++ {
elemv := srcv.Index(i)
if _, ok := visited[elemv.Interface()]; ok {
continue
}
visited[elemv.Interface()] = struct{}{}
dstv = reflect.Append(dstv, elemv)
}
return dstv.Interface()
}
Use it like this:
uniqueIntSlice := unique(intSlice).([]int)
Run the code on the Go Playground.

How to make sure this function works for a (unsorted) slice of empty interface{}
Considered that empty interface{} are comparable (https://stackoverflow.com/a/54003329/4466350)
Thus, to answer your question, it is very simple to rewrite your original code
package main
import (
"fmt"
)
func main() {
intSlice := []interface{}{1, 5, 3, 6, 9, 9, 4, 2, 3, 1, 5}
fmt.Println(unique(intSlice))
}
func unique(src []interface{}) []interface{} {
keys := make(map[interface{}]bool)
list := []interface{}{}
for _, entry := range src {
if _, value := keys[entry]; !value {
keys[entry] = true
list = append(list, entry)
}
}
return list
}
https://play.golang.org/p/vW7vgwz9yc1
If your question become, how to remove duplicates of any slice type, please check this other answer https://stackoverflow.com/a/65191679/4466350

Nothing elegant and very prone to errors, but you can us a function that receives two interface{} arguments, the first one is the slice to filter and the second is a pointer to the filtered slice, obviously if the first parameter is a slice of int, the second one MUST be s pointer to slice of int.
Inside the function you can check for the types of the parameters and treat them separately.
package main
import (
"fmt"
)
func unique(slice interface{}, filtered interface{}) error {
// Check for slice of string
if sliceOfString, ok := slice.([]string); ok {
// If slice is slice of string filtered MUST also be slice of string
filteredAsSliceOfString, ok := filtered.(*[]string)
if !ok {
return fmt.Errorf("filtered should be of type %T, got %T instead", &[]string{}, filtered)
}
keys := make(map[string]bool)
for _, entry := range sliceOfString {
if _, value := keys[entry]; !value {
keys[entry] = true
*filteredAsSliceOfString = append(*filteredAsSliceOfString, entry)
}
}
}else if sliceOfInt, ok := slice.([]int); ok {
// If slice is slice of int filtered MUST also be slice of int
filteredAsInt, ok := filtered.(*[]int)
if !ok {
return fmt.Errorf("filtered should be of type %T, got %T instead", &[]string{}, filtered)
}
keys := make(map[int]bool)
for _, entry := range sliceOfInt {
if _, value := keys[entry]; !value {
keys[entry] = true
*filteredAsInt = append(*filteredAsInt, entry)
}
}
} else {
return fmt.Errorf("only slice of in or slice of string is supported")
}
return nil
}
func main() {
intSlice := []int{1,5,3,6,9,9,4,2,3,1,5}
intSliceFiltered := make([]int, 0)
stringSlice := []string{"a", "b", "b", "c", "c", "c", "d"}
stringSliceFiltered := make([]string, 0)
fmt.Println(intSlice)
err := unique(intSlice, &intSliceFiltered) // Very important to send pointer in second parameter
if err != nil {
fmt.Printf("error filtering int slice: %v\n", err)
}
fmt.Println(intSliceFiltered)
fmt.Println(stringSlice)
err = unique(stringSlice, &stringSliceFiltered) // Very important to send pointer in second parameter
if err != nil {
fmt.Printf("error filtering string slice: %v\n", err)
}
fmt.Println(stringSliceFiltered)
}
As I said, it's not elegant. I haven't check this for errors.
Here it is running.

Related

How to common function of package model in golang?

I have 2 package as model:
class:
package class
import (
"encoding/json"
"student_management/model/base"
)
type Classes struct {
Classes []Class
}
type Class struct {
Id int `json:"student_id"`
Name int `json:"name"`
HomeroomTeacherId int `json:"homeroom_teacher_id"`
}
func ReadData() (chan Class, int) {
var classes Classes
byteValue := base.ReadJSON("db/student_class.json")
json.Unmarshal(byteValue, &classes)
classChannel := make(chan Class)
go func () {
for i := 0; i < len(classes.Classes); i++ {
classChannel <- classes.Classes[i]
}
close(classChannel)
}()
return classChannel, len(classes.Classes)
}
teacher:
package teacher
import (
"encoding/json"
base "student_management/model/base"
)
type Teachers struct {
Teachers []Teacher `json:"teachers"`
}
type Teacher struct {
base.Persions
HomeroomTeacher bool `json:"homeroom_teacher"`
}
func ReadData() (chan Teacher, int) {
var teachers Teachers
byteValue := base.ReadJSON("db/teachers.json")
json.Unmarshal(byteValue, &teachers)
teacherChannel := make(chan Teacher)
go func () {
for i := 0; i < len(teachers.Teachers); i++ {
teacherChannel <- teachers.Teachers[i]
}
close(teacherChannel)
}()
return teacherChannel, len(teachers.Teachers)
}
So you can see the ReadData function being repeated. And now I can use class.ReadData() and teacher.ReadData() to call data from channel.
How can I write ReadData() function once for both packages to use?
I tried creating a base package use generics like this:
package base
func ReadData[Models any, Model any](fileName string, m Models) (chan interface{}, int) {
byteValue := ReadJSON(fileName)
json.Unmarshal(byteValue, &m)
channel := make(chan Model)
go func () {
for i := 0; i < len(m.Models); i++ {
channel <- m.Models[i]
}
close(channel)
}()
return channel, len(models.Models)
}
but m.Models not found, i mean teachers.Teachers or classes.Classes can not be used
Please tell me what to do in this case
Use generics (introduced in Go 1.18). Create a single ReadData() function, use a parameter type denoting the values you want to decode from JSON and deliver on the channel.
Note: you should check for errors and report them (including from base.ReadJSON()).
func ReadData[T any](fileName, fieldName string) (chan T, int, error) {
var m map[string][]T
byteValue := base.ReadJSON(fileName)
if err := json.Unmarshal(byteValue, &wrapper); err != nil {
return nil, 0, err
}
values := m[fieldName]
valuesChannel := make(chan T)
go func() {
for _, v := range values {
valuesChannel <- v
}
close(valuesChannel)
}()
return valuesChannel, len(values), nil
}
Example calling it:
ch, n, err := ReadData[class.Class]("db/student_class.json", "classes")
// or
ch, n, err := ReadData[teacher.Teacher]("db/teachers.json", "teachers")
Note that it should be redundant to return the number of read values. Since you properly close the returned channel, the caller can use a for range over the returned channel which will receive all values sent on it properly, and then terminate.
Also note that since all values are ready (decoded) when you return the channel, this concurrency is redundant and just makes things less efficient. You have a slice of the decoded values, just return it and let the caller choose how it wishes to process it (concurrently or not).
So your ReadData() should look like this:
func ReadData[T any](fileName, fieldName string) ([]T, error) {
var m map[string][]T
byteValue := base.ReadJSON(fileName)
if err := json.Unmarshal(byteValue, &wrapper); err != nil {
return nil, err
}
return m[fieldName]
}
Also note that if the input JSON object has a single field, it's not necessary to pass the fieldName, you can get the value from the decoded m map like this:
func ReadData[T any](fileName string) ([]T, error) {
var m map[string][]T
byteValue := base.ReadJSON(fileName)
if err := json.Unmarshal(byteValue, &wrapper); err != nil {
return nil, err
}
for _, v := range m {
return v, nil
}
return nil, errors.New("empty JSON")
}
And then calling it is simply:
classes, err := ReadData[class.Class]("db/student_class.json")
// or
teachers, err := ReadData[teacher.Teacher]("db/teachers.json")

Go: Implementing a ManyDecode for a "set" of individual results

I have implemented a very simple Decode method (using gob.Decoder for now) - this works well for single responses - it would even work well for slices, but I need to implement a DecodeMany method where it is able to decode a set of individual responses (not a slice).
Working Decode method:
var v MyType
_ = Decode(&v)
...
func Decode(v interface{}) error {
buf, _ := DoSomething() // func DoSomething() ([]byte, error)
// error handling omitted for brevity
return gob.NewDecoder(bytes.NewReader(buf)).Decode(v)
}
What I'm trying to do for a DecodeMany method is to deal with a response that isn't necessarily a slice:
var vv []MyType
_ = DecodeMany(&vv)
...
func DecodeMany(vv []interface{}) error {
for _, g := range DoSomething() { // func DoSomething() []struct{Buf []bytes}
// Use g.Buf as an individual "interface{}"
// want something like:
var v interface{} /* Somehow create instance of single vv type? */
_ = gob.NewDecoder(bytes.NewReader(g.Buf)).Decode(v)
vv = append(vv, v)
}
return
}
Besides not compiling the above also has the error of:
cannot use &vv (value of type *[]MyType) as type []interface{} in argument to DecodeMany
If you want to modify the passed slice, it must be a pointer, else you must return a new slice. Also if the function is declared to have a param of type []interface{}, you can only pass a value of type []interface{} and no other slice types... Unless you use generics...
This is a perfect example to start using generics introduced in Go 1.18.
Change DecodeMany() to be generic, having a T type parameter being the slice element type:
When taking a pointer
func DecodeMany[T any](vv *[]T) error {
for _, g := range DoSomething() {
var v T
if err := gob.NewDecoder(bytes.NewReader(g.Buf)).Decode(&v); err != nil {
return err
}
*vv = append(*vv, v)
}
return nil
}
Here's a simple app to test it:
type MyType struct {
S int64
}
func main() {
var vv []MyType
if err := DecodeMany(&vv); err != nil {
panic(err)
}
fmt.Println(vv)
}
func DoSomething() (result []struct{ Buf []byte }) {
for i := 3; i < 6; i++ {
buf := &bytes.Buffer{}
v := MyType{S: int64(i)}
if err := gob.NewEncoder(buf).Encode(v); err != nil {
panic(err)
}
result = append(result, struct{ Buf []byte }{buf.Bytes()})
}
return
}
This outputs (try it on the Go Playground):
[{3} {4} {5}]
When returning a slice
If you choose to return the slice, you don't have to pass anything, but you need to assign the result:
func DecodeMany[T any]() ([]T, error) {
var result []T
for _, g := range DoSomething() {
var v T
if err := gob.NewDecoder(bytes.NewReader(g.Buf)).Decode(&v); err != nil {
return result, err
}
result = append(result, v)
}
return result, nil
}
Using it:
vv, err := DecodeMany[MyType]()
if err != nil {
panic(err)
}
fmt.Println(vv)
Try this one on the Go Playground.

Is it possible to assert types dynamically in golang?

I have a method Deduplicate that returns deduplicated copy of passed in slice as an interface{}. Is there a way to cast returned by this method interface{} value to the same type as I passed in this method without writing it explicitly? For example, if I change myStruct.RelatedIDs type from []int to []uint it will prevent code from compiling.
https://play.golang.org/p/8OT4xYZuwEn
package main
import (
"fmt"
"reflect"
)
type myStruct struct {
ID int
RelatedIDs []int
}
func main() {
s := &myStruct{
ID: 42,
RelatedIDs: []int{1, 1, 2, 3},
}
v, _ := Deduplicate(s.RelatedIDs)
s.RelatedIDs = v.([]int) // << can I assert type dynamically here?
// s.RelatedIDs = v.(reflect.TypeOf(s.RelatedIDs)) // does not work
fmt.Printf("%#v\n", s.RelatedIDs)
}
func Deduplicate(slice interface{}) (interface{}, error) {
if reflect.TypeOf(slice).Kind() != reflect.Slice {
return nil, fmt.Errorf("slice has wrong type: %T", slice)
}
s := reflect.ValueOf(slice)
res := reflect.MakeSlice(s.Type(), 0, s.Len())
seen := make(map[interface{}]struct{})
for i := 0; i < s.Len(); i++ {
v := s.Index(i)
if _, ok := seen[v.Interface()]; ok {
continue
}
seen[v.Interface()] = struct{}{}
res = reflect.Append(res, v)
}
return res.Interface(), nil
}
Try this
package main
import (
"fmt"
"reflect"
)
type myStruct struct {
ID int
RelatedIDs []int
}
func main() {
s := &myStruct{
ID: 42,
RelatedIDs: []int{1, 1, 2, 3},
}
err := Deduplicate(&s.RelatedIDs)
fmt.Println(err)
// s.RelatedIDs = v.([]int) // << can I assert type dynamically here?
// s.RelatedIDs = v.(reflect.TypeOf(s.RelatedIDs)) // does not work
fmt.Printf("%#v\n", s.RelatedIDs)
}
func Deduplicate(slice interface{}) error {
rts := reflect.TypeOf(slice)
rtse := rts.Elem()
if rts.Kind() != reflect.Ptr && rtse.Kind() != reflect.Slice {
return fmt.Errorf("slice has wrong type: %T", slice)
}
rvs := reflect.ValueOf(slice)
rvse := rvs.Elem()
seen := make(map[interface{}]struct{})
var e int
for i := 0; i < rvse.Len(); i++ {
v := rvse.Index(i)
if _, ok := seen[v.Interface()]; ok {
continue
}
seen[v.Interface()] = struct{}{}
rvse.Index(e).Set(v)
e++
}
rvse.SetLen(e)
rvs.Elem().Set(rvse)
return nil
}
https://play.golang.org/p/hkEW4u1aGUi
with future generics, it might look like this https://go2goplay.golang.org/p/jobI5wKR8fU
For completeness, here is a generic version (Playground), which doesn't require reflection. Only open in February 2022! The usual caveats about NaN apply, though.
package main
import (
"fmt"
)
func main() {
s := []int{1, 1, 2, 3}
res := Deduplicate(s)
fmt.Printf("%#v\n", res)
}
func Deduplicate[T comparable](s []T) []T {
seen := make(map[T]struct{})
res := make([]T, 0, len(s))
for _, elem := range s {
if _, exists := seen[elem]; exists {
continue
}
seen[elem] = struct{}{}
res = append(res, elem)
}
return res
}
Output:
[]int{1, 2, 3}

Go can't set string to struct

This code is get all objects from s3 and delete objects.
getAllObjects is called from DeletePhotosFromS3.
I cloud get 2 different keys in objects that is in DeletePhotosFromS3.
But deleteObjects have 2 same keys. ex [{Key: 1}, {Key: 1}].
Why deleteObjects have 2 same keys and how to set objects in []*s3.ObjectIdentifier?
func getAllObject(userID string) (*[]string, error) {
var objects []string
svc := initS3()
config := model.NewConfig()
input := &s3.ListObjectsInput{
Bucket: aws.String(config.AWSS3Bucket),
Prefix: aws.String(userID),
MaxKeys: aws.Int64(2), // default 1000
}
result, err := svc.ListObjects(input)
if err != nil {
return &objects, err
}
for _, v := range result.Contents {
objects = append(objects, *v.Key)
}
return &objects, nil
}
func DeletePhotosFromS3(userID string) (error) {
var deleteObjects []*s3.ObjectIdentifier
svc := initS3()
config := model.NewConfig()
objects, err := getAllObject(userID) // called getAllObject
for _, v := range *objects {
deleteObjects = append(deleteObjects, &s3.ObjectIdentifier{Key: &v}) // Er
}
...
}
The iteration value v in your for loop is reused for each iteration. The Pointer &v will be the same for each item appended to the list. Fixed snippet:
for _, v := range *objects {
vcopy := v
deleteObjects = append(deleteObjects, &s3.ObjectIdentifier{Key: &vcopy})
}

How do I convert this cache item back to a slice of maps?

I'm still new to Go and trying to use Beego's cache. I can put a []map[string]string into the cache but can't figure out how to convert the value back to a []map[string]string.
For instance, to put the item in the cache:
m:=make([]map[string]string)
// add items to the slice of maps
.......
// cache it
if err := c.Put("key", m, 100); err != nil {
fmt.Println(err)
}
// retrieve it
n := c.Get("key")
fmt.Println(reflect.TypeOf(n)) // ==>string
// failed attempt
a := n.([]map[string]string)
fmt.Println(a) // panic: interface conversion: interface is string, not []map[string]string
How do I convert n to a slice of maps?
well digging into the code seems like even if it says interface{} what it does it actually squash everything to []byte
https://github.com/astaxie/beego/blob/master/cache/memcache/memcache.go#L77
and when it Does the Get convert everything to string
https://github.com/astaxie/beego/blob/master/cache/memcache/memcache.go#L61
So you need to Marshal / Unmarshal the data structure yourself.
See this example and substitute your calls and error check with the dummies one provided:
http://play.golang.org/p/9z3KcOlgAx
package main
import (
"bytes"
"encoding/json"
"log"
)
var cache map[string]string = make(map[string]string)
func Put(key, value string) {
cache[key] = value
}
func Get(key string) string {
return cache[key]
}
func main() {
m := map[string]string{
"A": "1",
"B": "2",
}
if b, err := json.Marshal(m); err != nil {
log.Fatal(err)
} else {
Put("myKey", string(b))
}
b := bytes.NewBufferString(Get("myKey"))
var mm map[string]string
if err := json.Unmarshal(b.Bytes(), &mm); err != nil {
log.Fatal(err)
}
log.Printf("%#v", mm)
}

Resources