Sort/order a slice string by given slice - go

How do I sort a slice of string in a order that is given by another slice of strings. If that string is not there in the input slice then just ignore it.
animalsInput := []string{"cat", "bird", "zebra", "fox"}
animalsOrder := []string{"bird", "lion", "fox"}
//desired output
//{"bird", "fox", "cat", "zebra"}

One way you can implement this is by writing a rank map based on the order array
rank:=map[string]int{}
for i, x:=range animalsOrder {
rank[x]=i
}
Then use rank in sort:
sort.Slice(animalsInput,func(i,j int) bool {
irank, ok:=rank[animalsInput[i]]
if !ok {
irank=len(animalsInput)
}
jrank, ok:=rank[animalsInput[j]]
if !ok {
jrank=len(animalsInput)
}
return irank<jrank
})

You can try this code:
package main
import (
"fmt"
)
func sort(in, order []string) (out []string) {
flag := make([]bool, len(in))
out = make([]string, len(in))
orderCountMap := make(map[string]int)
for i := range in {
orderCountMap[in[i]] += 1
}
for i := range in {
if _, found := orderCountMap[in[i]]; found {
flag[i] = true
} else {
flag[i] = false
}
}
p := 0
for i := range order {
if v, found := orderCountMap[order[i]]; found {
for j := 0; j < v; j++ {
out[p] = order[i]
p += 1
}
}
}
for i := range flag {
if !flag[i] {
out[p] = in[i]
p += 1
}
}
return
}
func main(){
animalsInput := []string{"cat", "bird", "zebra", "fox"}
animalsOrder := []string{"bird", "lion", "fox", "zebra", "cat"}
out := sort(animalsInput, animalsOrder)
fmt.Println(out)
}

Related

How to remove elements from other_names that are equal to elements in names?

Compare slice 'names' with slice 'other_names';
If slice of slice 'names'(names[1]) have same items that slice of slice 'other_names'(other_name[0][1]), we should delete that slice.
So, I need to compare 'names' and 'other_names' and delete slice of the slice if it have same elements.
Simple. How to delete 'other_name[0][1]', it should be:
var other_names = [][][]string{
{
{"Sony", "Bond"},
{"Piter", "Nina"},
},
}
The following code works, but not correctly =>
func Duplicate() {
var names = [][]string{
{"Malder", "Carl"},
{"Adam", "Kent"},
}
var other_names = [][][]string{
{
{"Sony", "Bond"},
{"Adam", "Kent"},
{"Piter", "Nina"},
},
}
// s := append(s, copies)
var origl [][]string
for i := 0; i < len(names); i++ {
// fmt.Println(names[i][1])
for v := 0; v < len(copies); v++ {
for d := 0; d < len(copies[v]); d++ {
if names[i][1] == copies[v][d][1] {
// fmt.Println("[copy index]", d, copies[v][d])
origl = copies[v][:d]
// fmt.Println(origl)
}
}
}
}
fmt.Println(origl)
}
How to delete 'other_name[0][1]
Think of this as a filtering problem, not a deletion problem. Construct a slice of what you want to keep.
Write a function to determine whether a []string is contained in a [][]string.
func contains(needle []string, haystack [][]string) bool {
hloop:
for _, h := range haystack {
if len(needle) != len(h) {
continue hloop
}
for i := range needle {
if needle[i] != h[i] {
continue hloop
}
}
return true
}
return false
}
Copy elements from the original slice to the result slice only if the element in the original slice is not contained in names.
result := other_names[0][:0]
for _, other_name := range other_names[0] {
if !contains(other_name, names) {
result = append(result, other_name)
}
}
other_names[0] = result

Merging Intervals out of hyphenated string ranges

I have a string like this:
ports := []string{"1", "2-7", "12-1200", "10-500"}
I would like to make an integer set out of this like the output should be :
[]intSet{ 1, 2-7, 10-1200 }
Where intSet is some kind of integer set from which I am able to easily remove and add elements.
Update 1
intSet is a list of sets.
So, 2-7 is also a set.
Update 2
Here the largest set is merged.
e.g.
"1" -> 1
"2-7" -> 2-7
"12-1200" & "10-500" => "10..12.....500....1200" -> 10-1200
Since it's a set so it encompasses a unique range for this, a range which covers the whole set.
package main
import (
"fmt"
"log"
"strconv"
"strings"
)
type intSet struct {
start int
end int
}
func (s intSet) String() string {
if s.start == s.end {
return fmt.Sprintf("%d", s.start)
}
return fmt.Sprintf("%d-%d", s.start, s.end)
}
func (s intSet) in(i int) bool {
return s.start <= i && i <= s.end
}
func (s *intSet) union(set intSet) {
if set.start < s.start {
s.start = set.start
}
if set.end > s.end {
s.end = set.end
}
}
func insert(set intSet, is []intSet) bool {
for i, s := range is {
if s.in(set.start) || s.in(set.end) {
is[i].union(set)
return true
}
//updated here with thankful to #mh-cbon
if set.in(s.start) || set.in(s.end) {
is[i].union(set)
return true
}
}
return false
}
func main() {
var set []intSet
ports := []string{"1", "2-7", "12-1200", "10-500", "9-5500"}
for _, port := range ports {
s := strings.Split(port, `-`)
if len(s) < 1 || len(s) > 2 {
log.Fatalln(`set cannot have multiple values or no value`)
}
start, err := strconv.Atoi(s[0])
if err != nil {
log.Fatalln(err)
}
end := start
if len(s) == 2 {
end, err = strconv.Atoi(s[1])
if err != nil {
log.Fatalln(err)
}
}
temSet := intSet{
start: start,
end: end,
}
if !insert(temSet, set) {
set = append(set, temSet)
}
}
fmt.Println(set) //[1 2-7 9-5500]
}
run here

Golang map put if absent?

I have a map of the format:
map[string]map[string]int
In this main map, I want to do something like putIfAbsent("key", new HashMap<>() as we have in Java. What is a clean and shorthand way to do it in Go?
You can do:
var val map[string]int
val, exists := m[key]
if !exists {
val = make(map[string]int)
m[key] = val
}
If you don't need the val in the code coming below this:
if _,exists := m[key]; !exists {
m[key]=make(map[string]int)
}
If you don't intend to use the value right away, here you go...
m := make(map[string]map[string]int)
if _, ok := m["unknown"]; !ok {
m["unknown"] = make(map[string]int)
}
Below is a suggestion for improvement:
To keep things clean and easy to understand, you can define your own types. For example, if your data is a mapping of "cities to persons to age", I would do it like this:
type Person map[string]int
type City map[string]Person
m := make(City)
if _, ok := m["Dhaka"]; !ok {
m["Dhaka"] = make(Person)
}
func main() {
var testMap map[int]interface{}
testMap = make(map[int]interface{})
var addMap map[int]string
addMap = make(map[int]string)
addMap[1] = "999"
addMap[2] = "888"
Add(testMap, 111, addMap)
for key, val := range testMap {
fmt.Println(key)
for key2, val2 := range val.(map[int]string) {
fmt.Println(key2, val2)
}
}
}
func Add(_testMap map[int]interface{}, _key int, _val map[int]string) {
_, exist := _testMap[_key] // _ -> value
if exist == false {
//addmap
_testMap[_key] = _val
} else {
//whatever wanna to do
}
}

How to convert a slice of int to a slice of string

Well, I created a slice of int like this:
list_of_id := []string {1,2,3,4}
My code would do a check if a variable in my slice (list_of_id):
func contains(s [] int, input int) bool {
for _, v := range s {
if v == input {
return true
}
}
return false
}
func main() {
list_of_id := [] int {1,2,3,4}
fmt.Println(contains(list_of_id, 1))
}
I want to create a function with the flexibility that I could input 1 or "1" as well.
My intention is to create an if else condition in which the slice of int [] int {1,2,3,4} will be converted into a slice of string [] string {"1","2","3","4"} to check again.
Anddddd, I don't know how to do so. I tried to google it out but all I found is a solution to convert this [] int {1,2,3,4} to this "{1,2,3,4}"
import "strconv"
func contains(s [] int, input interface{}) bool {
switch i := input.(type) {
case int:
for _, v := range s {
if v == i {
return true
}
}
case string:
for _, v := range s {
if strconv.Itoa(v) == i {
return true
}
}
}
return false
}
https://play.golang.org/p/02J1f77n_aM
package main
import (
"fmt"
"strconv"
)
func contains(s []int, input interface{}) bool {
var c int
var err error
switch input.(type) {
case int:
c = input.(int)
case string:
tmp := input.(string)
c, err = strconv.Atoi(tmp)
}
if err != nil {
return false
}
for _, v := range s {
if v == c {
return true
}
}
return false
}
func main() {
list_of_id := []int{1, 2, 3, 4}
fmt.Println(contains(list_of_id, 3))
fmt.Println(contains(list_of_id, 5))
fmt.Println(contains(list_of_id, "1"))
fmt.Println(contains(list_of_id, "6"))
}
Here example

Iterate through all the methods using range for loop

I have a struct with methods defined in it, i want to iterate through all the methods. Can i use interface {} or any other way to achieve this ?
type SomeStruct struct {
m_boolVals []bool
m_stringVals []string
m_intVals []int64
}
func (m *SomeStruct) GetBool() []bool {
return m.m_boolVals
}
func (m *SomeStruct) GetString() []string {
return m_stringVals
}
func (m *SomeStruct) GetInts() []int64 {
return m_intVals
}
Is there a way to achieve below code ? So basically only one of value would be present
fun SomeOtherFunc(ss *SomeStruct) []string {
var final_string []string
for _, handlerFunc := range(ss.GetBool, ss.GetString, ss.GetInts) {
generic_vals := handlerFunc()
if (len(generic_vals) > 0) {
for _, t_val := range(generic_vals) {
final_string = append(final_string , string(t_val))
}
break
}
}
return final_string
}
Here's an example how you can use reflection to iterate over the methods and call each, and convert their result to string:
func SomeOtherFunc(ss *SomeStruct) []string {
var result []string
v := reflect.ValueOf(ss)
for i := 0; i < v.NumMethod(); i++ {
for _, res := range v.Method(i).Call(nil) {
result = append(result, fmt.Sprint(res.Interface()))
}
}
return result
}
Testing it:
fmt.Println(SomeOtherFunc(&SomeStruct{
m_boolVals: []bool{true, false},
m_stringVals: []string{"one", "two"},
m_intVals: []int64{1, 2, 3},
}))
Which outputs (try it on the Go Playground):
[[true false] [1 2 3] [one two]]

Resources