Keyword search with negative keywords - go

I have a simple question about keywords searching in a Go.
I want to search a string using positive and negative keywords
func keyword(itemTitle string, keywords string) bool {
splits := strings.Split(keywords, ",")
for _, item := range splits {
item = strings.TrimSpace(item)
fmt.Println(strings.ToUpper(itemTitle))
fmt.Println(strings.ToUpper(item))
if strings.Contains(item,"-") {
item = item[1:]
if strings.Contains(strings.ToUpper(itemTitle), strings.ToUpper(item)) {
return false
}
}
item = item[1:]
fmt.Println(strings.ToUpper(item))
if strings.Contains(strings.ToUpper(itemTitle), strings.ToUpper(item)) {
return true
}
}
return false
}
heres my searcher method
func TestKeyword(t *testing.T) {
test1 := "Pokemon Nintendo Switch Cool Thing"
keywordTest1 := "+pokemon,-nintendo"
if keyword(test1, keywordTest1) {
fmt.Println("matched")
} else {
fmt.Println("test")
}
test2 := "Pokemon Cards Cool"
if keyword(test2, keywordTest1) {
fmt.Println("matched")
} else {
fmt.Println("test")
}
}
my test cases
i understand why its not working because +amd is the first in the slice and its ofc going to return true and not test any of the other like -radeon but im just kinda stumped on what todo.
Output given
matched
matched
Expected Output
test
matched

I updated your search function but kept the signature
func keyword(itemTitle string, keywords string) bool {
a := strings.ToUpper(itemTitle)
b := strings.ToUpper(keywords)
keys := strings.Split(strings.Replace(b, "-", " ", -1), ",")
for _, key := range keys {
key = strings.TrimSpace(key)
if strings.Contains(a, key) {
return true
}
}
return false
}
And updated your test function with a passing test and a failed one to see how it works.
func TestKeyword(t *testing.T) {
test1 := "Pokemon Nintendo Switch Cool Thing"
keywordTest1 := "+pokemon,-nintendo"
if keyword(test1, keywordTest1) {
t.Log("matched")
} else {
t.Fail()
}
test2 := "Pokemon Cards Cool"
if keyword(test2, keywordTest1) {
t.Log("matched")
} else {
t.Fail()
}
}
Regarding the second test failing with a keyword with + on it, you could pass that through a regex to get only alphanumeric characters, if required.

Related

count slice length in struct field

I think I need a better solution than my case switch as the struct gains more fields my function will become verbose. Is there a way to swap my switch for a loop?
I have the following code
type Things struct {
StreetNames []string `json:"streetNames"`
Letters []string `json:"letters"`
MaleNames []string `json:"maleNames"`
}
func CheckCategories(data *Things, filePath string) error {
errMsg := "list has no values or is a missing category in File: " + filePath
categories := []string{"street_name", "letter", "male_first_name"}
for _, value := range categories {
switch value {
case "street_name":
if len(data.StreetNames) == 0 {
return errors.New("street_name " + errMsg)
}
case "letter":
if len(data.Letters) == 0 {
return errors.New("letter " + errMsg)
}
case "male_first_name":
if len(data.MaleNames) == 0 {
return errors.New("male_first_name " + errMsg)
}
}
}
return nil
}
This works for me but the real struct contains 12 fields which makes my functions long and repetitive.
I tried
for _, value := range categories {
if len("data." + value) == 0 {
return errors.New(value + errMsg)
}
But when I ran the code I took a moment to notice it wasn't working as intended, Im getting the length of the string. I have tried data[value] but that didn't work either.
Is there a way to swap my switch for a loop?
You could do the following:
type Things struct {
StreetNames []string `json:"streetNames"`
Letters []string `json:"letters"`
MaleNames []string `json:"maleNames"`
}
func CheckCategories(data *Things, filePath string) error {
errMsg := "list has no values or is a missing category in File: " + filePath
categories := []struct{
name string
slice []string
}{
{"street_name", data.StreetNames},
{"letter", data.Letters},
{"male_first_name", data.MaleNames},
}
for _, v := range categories {
if len(v.slice) == 0 {
return errors.New(v.name + " " + errMsg)
}
}
return nil
}

Prettify ugly three nested for-loop

What would be the most Go way of prettifying this function?
This is what I have come up with, kind of does the trick but It's just too ugly, any help on prettifying this would be greatly appreciated.
Also wold love to be able to negate this functions as well if possible.
Could I not utilise the use of function literals, maps etc.
var UsageTypes = []string{
"PHYSICAL_SIZE",
"PHYSICAL_SIZE",
"PROVISIONED_SIZE",
"SNAPSHOT_SIZE",
"LOGICAL_SIZE_PERCENTAGE",
"TOTAL_VOLUME_SIZE",
"ALLOCATED_SIZE",
"ALLOCATED_USED",
"TOTAL_LOGICAL_SIZE",
"TOTAL_LOGICAL_SIZE_PERCENTAGE",
"TOTAL_SNAPSHOT_SIZE",
"LOGICAL_OR_ALLOCATED_GREATER_SIZE",
}
var MeasuredTypes = []string{
"LIF_RECEIVED_DATA",
"ECEIVED_ERRORS",
"LIF_RECEIVED_PACKET",
"LIF_SENT_DATA",
"LIF_SENT_ERRORS",
"LIF_SENT_PACKET",
"LINK_CURRENT_STATE",
"RX_BYTES",
"RX_DISCARDS",
"RX_CRC_ERRORS",
"RX_ERRORS",
"RX_FRAMES",
"LINK_UP_TO_DOWNS",
"TX_BYTES",
"TX_DISCARDS",
"TX_ERRORS",
"TX_HW_ERRORS",
"TX_FRAMES",
"LOGICAL_OR_ALLOCATED_GREATER_SIZE",
"LOGICAL_SIZE",
"PHYSICAL_SIZE",
"PROVISIONED_SIZE",
"SNAPSHOT_SIZE",
"VOLUME_ONLINE",
"TOTAL_THROUGHPUT",
"LOGICAL_SIZE_PERCENTAGE",
"READ_THROUGHPUT",
"WRITE_THROUGHPUT",
"OTHER_THROUGHPUT",
"TOTAL_IOPS",
"WRITE_IOPS",
"READ_IOPS",
"OTHER_IOPS",
"AVERAGE_TOTAL_LATENCY",
"AVERAGE_WRITE_LATENCY",
"AVERAGE_READ_LATENCY",
"AVERAGE_OTHER_LATENCY",
"FILESYSTEM_READ_OPS",
"FILESYSTEM_WRITE_OPS",
"FILESYSTEM_TOTAL_OPS",
"FILESYSTEM_OTHER_OPS",
"IO_BYTES_PER_READ_OPS",
"IO_BYTES_PER_WRITE_OPS",
"IO_BYTES_PER_OTHER_OPS",
"IO_BYTES_PER_TOTAL_OPS",
"READ_IO",
"WRITE_IO",
"TOTAL_IO",
"OTHER_IO",
"ACTIVE_CONNECTIONS",
"TOTAL_VOLUME_SIZE",
"ALLOCATED_SIZE",
"ALLOCATED_USED",
"TOTAL_LOGICAL_SIZE",
"TOTAL_LOGICAL_SIZE_PERCENTAGE",
"TOTAL_SNAPSHOT_SIZE",
"ONTAP_CAPACITY_DISK_CAPACITY",
"ONTAP_CAPACITY_TOTAL_STORAGE_EFFICIENCY_RATIO",
"ONTAP_CAPACITY_TOTAL_PHYSICAL_USED",
"ONTAP_CAPACITY_SIZE_USED",
"ONTAP_CAPACITY_MEMORY",
"ONTAP_CAPACITY_AVERAGE_PROCESSOR_BUSY",
"ONTAP_CAPACITY_PEAK_PROCESSOR_BUSY",
}
func isMeasuredTypeAUsageMetric(measuredTypeIn []string) []string {
result := []string{}
for i, _ := range measuredTypeIn {
var foundInBigList bool
for j, _ := range MeasuredTypes {
if measuredTypeIn[i] == MeasuredTypes[j] {
foundInBigList = true
fmt.Println("found in big list: ", measuredTypeIn[i])
for k, _ := range UsageTypes {
if measuredTypeIn[i] == UsageTypes[k] {
fmt.Println("found in inner list: ", measuredTypeIn[i])
result = append(result, measuredTypeIn[i])
}
}
}
}
if foundInBigList == false {
fmt.Println("not found, throw exception")
}
}
return result
}
func main() {
measuredTypeIn := []string{"LOGICAL_SIZE_PERCENTAGE", "LOGICAL_OR_ALLOCATED_GREATER_SIZE", "BUKK", "ONTAP_CAPACITY_PEAK_PROCESSOR_BUSY",}
fmt.Println(isMeasuredTypeAUsageMetric(measuredTypeIn))
}
Right level of abstraction is what you need:
func has(in string[], item string) bool {
for _,x:=range in {
if x==item {
return true
}
}
return false
}
func isMeasuredTypeAUsageMetric(measuredTypeIn []string) []string {
result:=[]string{}
for _,item:=range measuredTypeIn {
if has(MeasuredTypes,item) {
if has(UsageTypes,item) {
result=append(result,item)
}
} else {
///error
}
}
return result
}
This can be further simplified by using a map[string]bool instead of a []string for the literals.
var MeasuredTypes=map[string]bool{"itemInUsageTypes": true,
"itemNotInUsageTypes":false,
...
}
Then you can do:
usage,measured:=MeasuredTypes[item]
if measured {
// It is measured type
if usage {
// It is usage type
}
}

Using OR logic when checking for multiple keys in map[string]interface{}

I have a map[string]interface{} called mapped:
mapped map[stringinterface{}
I want to iterate through it checking to see if either of these keys exist:
columns
rows
Then if so, I want to append the row or column to a slice of strings called:
columnOrRowArray
I understand that if I just needed to find, for example columns, within mapped I can do this:
var columnOrRowArray []string
if columnsOrRows, ok := mapped["columns"].([]interface{}); ok {
for _, columnOrRow := range columnsOrRows {
if columnOrRowValueIsString, ok = columnOrRow.(string); ok {
columnOrRowArray = append(columnOrRowArray, columnOrRowValueIsString)
}
}
}
What would be a clean way without me duplicating the above for using row logic for the mapped["rows"]?
I want to do something that is basically this:
columnsOrRows, ok := mapped["columns"].([]interface{}) || mapped["rows"].([]interface{}); ok {
So in plain English, "if mapped has a the column or row key, assign to variable columnsOrRows"
Obviously I know the syntax for that is wrong, but I can't find an example of someone doing this
Test for both keys:
columnsOrRows, ok := mapped["columns"].([]interface{})
if !ok {
columnsOrRows, ok = mapped["rows"].([]interface{})
}
if ok {
for _, columnOrRow := range columnsOrRows {
if columnOrRowValueIsString, ok = columnOrRow.(string); ok {
columnOrRowArray = append(columnOrRowArray, columnOrRowValueIsString)
}
}
}
I had to perform a broader check. Check if any of possible keys (more than 2) exist in the map. Ended up writing a utility function to accomplish the task and have the code remain readable.
func StringInSlice(s string, list []string) bool {
for _, item := range list {
if item == s {
return true
}
}
return false
}
func AnyKeyInMap(keys []string, keyMap map[string]interface{}) bool {
for k := range keyMap {
if StringInSlice(k, keys) {
return true
}
}
return false
}
The usage is:
mapped := make(map[string]interface{})
mapped["rows"] = true
if AnyKeyInMap([]string{"rows", "columns"}, mapped) {
fmt.Println("exists")
}
You can play with it here:
https://play.golang.org/p/pz64YidEGMK

Replace a single string in a slice for a group of strings

I want to have a slice of strings, and when certain strings are found, they are replaced by a group of related strings.
For instance, if I have this :
[]string{"A","FROM_B_TO_E","F"}
After my method runs I want to have :
[]string{"A","B","C","D","E","F"}
I came up with this code, the thing is, although I can print my to_be_added slice just before actually adding it, for some reason it does not work. It does work however if I change my translateRule so instead of returning a slice of string it only returns a single string :
func groupRules(validationRules []string){
for index,rulename := range validationRules {
if succeeded, to_be_added := translateRule(rulename) ; succeeded == true{
fmt.Println("Entro! ", to_be_added)
validationRules = append(append(validationRules[:index],to_be_added...), validationRules[index+1:]...)
}
}
}
func translateRule(rule string) ( bool , []string ) {
if rule == "rs_full" {
return true,[]string{"sapo","rana"}
}
return false,nil
}
So, my lack of Go experience or the bad code I write lead me to this :
func groupRules(validationRules []string) []string{
var tmp_slice []string
for _ ,rulename := range validationRules {
if succeeded, to_be_added := translateRule(rulename) ; succeeded == true{
tmp_slice = append(tmp_slice,to_be_added...)
}else{
tmp_slice = append(tmp_slice,rulename)
}
}
return tmp_slice
}
func translateRule(rule string) ( bool , []string ) {
if rule == "rs_full" {
return true,[]string{"sapo","rana","tigre"}
}
return false,nil
}
Now it works flawlessly.
Thank you all.

Making code more generic

I have a program where many functionalities are similar across different structures, however, I end up writing these functions again and again, esp because the variable that are being dealt inside are of different structures.
I have written a sample code here.
In Go Playgroud
package main
import "fmt"
func (a *Match) Add(v Match) {
a.Runs += v.Runs
a.Points += v.Points
}
type Match struct {
Runs uint64
Points uint64
}
func (a *Activity) Add(v Activity) {
a.Walk += v.Walk
a.Jog += v.Jog
}
type Activity struct {
Walk uint64
Jog uint64
}
func GetDailyMatches() map[string]Match {
var dailyMatches map[string]Match
Match1, Match2 := Match{5, 10}, Match{1, 2}
dailyMatches = make(map[string]Match)
dailyMatches["01"] = Match1
dailyMatches["02"] = Match2
dailyMatches["03"] = Match1
dailyMatches["04"] = Match2
return dailyMatches
}
func GetDailyActivities() map[string]Activity {
var dailyActivities map[string]Activity
Activity1, Activity2 := Activity{5, 10}, Activity{1, 2}
dailyActivities = make(map[string]Activity)
dailyActivities["01"] = Activity1
dailyActivities["02"] = Activity2
dailyActivities["03"] = Activity1
dailyActivities["04"] = Activity2
return dailyActivities
}
func main() {
fmt.Println(CalculateMatchSummary("01", "03"))
fmt.Println(CalculateActivitySummary("02", "04"))
fmt.Println(CalculateMatchSummary("01", "03"))
fmt.Println(CalculateActivitySummary("02", "04"))
}
func CalculateMatchSummary(start, end string) (total Match) {
dailyMatches := GetDailyMatches()
for day, value := range dailyMatches {
if day < start {
continue
} else if day > end {
continue
} else {
total.Add(value)
}
}
return
}
func CalculateActivitySummary(start, end string) (total Activity) {
dailyActivities := GetDailyActivities()
for day, value := range dailyActivities {
if day < start {
continue
} else if day > end {
continue
} else {
total.Add(value)
}
}
return
}
If you notice, both Match and Activity has the same functions and same structures, except that internally they are of different structures.
Is there a easy way to make the code more generic (Go generics, which is not there in Go??) in Golang itself.
Go has a pretty package "reflect". You can not do genericity strictly speaking but you can get unification of code for the same behavior.
I've changed your playground a bit : https://play.golang.org/p/bfqZsFOgVQ
The main part :
func AddTwo(a, b interface{}) interface{} {
va := reflect.ValueOf(a)
vb := reflect.ValueOf(b)
res := reflect.New(reflect.TypeOf(a)).Elem()
if va.Kind() != reflect.Struct && vb.Kind() != reflect.Struct {
return nil
}
na, nb := va.NumField(), vb.NumField()
if na != nb {
return nil
}
for i := 0; i < na; i++ {
// additional verification needed here
fa := va.Field(i).Uint()
fb := vb.Field(i).Uint()
fr := fa + fb
res.Field(i).SetUint(fr)
}
return res.Interface()
}
I use reflect to check the fields of the struct I am given. If both are uint64, I can add them reflectively. If your structs contains many uint64, it can add them all !
Note that you must convert the resulting interface to the type of the struct given after calling this function. That is why this is not strictly generic, because the returning type is a interface, and not a Match or Activity.
EDIT: No need even to return a new struct. You can simply update the field of the "a" struct by calling .SetUint() method.

Resources