How to recursively loop through map using different data structures - go

I'm trying to figure out the best way to recursively go through a [string]int map in Go. I'm building a game in which multiple countries are involved, and grouped together by teams of two in the end.
The goal is to match the first two countries with the lowest 'score' into its own group of two, and add it back to the collection giving the new map a total value of the scores of those countries.
Then recursively doing that to all the groups, ending up with one group and one total value in the end.
For example, if you had:
score := map[string]int{
"Canada": 7,
"US": 2,
"Germany": 3,
"Korea": 4,
}
group1 = {[US:2] [Germany:3]} with a total of 5
group1 would now be put back into the initial collection with a 'score' of 5 since it takes the two lowest scores. We would now have:
score := map[string]int{
"Canada": 7,
"Korea": 4,
group1: `US:2 Germany:3` with a total of 5
}
If this was now the lowest score in the collection, the next iteration would be:
group2 = {[Korea:4] [group1:5]}
score := map[string]int{
"Canada": 7,
group2: `Korea:4 group1:5` with a total of 9
}
And so on until you're left with one.. I think the basic structure should be something like this. However, I'm unsure of the proper way to do this since the data structure is now encompassing a [string]int map, as well as this new map.
I realize this is not such a generic question, but could an interface be used for this? I'm very new to Go, so advice would be helpful.
Here is an example to further illustrate what I mean:
https://play.golang.org/p/cnkTc0HBY4

Your problem can "easy" be solved using a heap data structure.
package main
import (
"container/heap"
"fmt"
)
// Something that has a score
type Scoreable interface {
fmt.Stringer
Score() int
}
// A country has a name and a score
type Country struct {
name string
score int
}
// Country implements Scoreable
func (c Country) Score() int {
return c.score
}
// ... and fmt.Stringer
func (c Country) String() string {
return fmt.Sprintf("%s [%d]", c.name, c.score)
}
// A team consists of two Scoreable's and has itself a score
type Team struct {
team1, team2 Scoreable
score int
}
// Team implements Scoreable
func (t Team) Score() int {
return t.score
}
// ... and fmt.Stringer
func (t Team) String() string {
return fmt.Sprintf("(%s + %s)", t.team1.String(), t.team2.String())
}
// The heap will be implemented using a slice of Scoreables
type TeamHeap []Scoreable
// TeamHeap implements heap.Interface
func (th TeamHeap) Len() int {
return len(th)
}
func (th TeamHeap) Less(i, j int) bool {
return th[i].Score() < th[j].Score()
}
func (th TeamHeap) Swap(i, j int) {
th[i], th[j] = th[j], th[i]
}
func (th *TeamHeap) Push(t interface{}) {
*th = append(*th, t.(Scoreable))
}
func (th *TeamHeap) Pop() interface{} {
old := *th
n := len(old)
t := old[n-1]
*th = old[0 : n-1]
return t
}
// The main function
func main() {
// Create a heap and initialize it
teams := &TeamHeap{}
heap.Init(teams)
// Push the countries (NB: heap.Push(), not teams.Push())
heap.Push(teams, Country{"Canada", 7})
heap.Push(teams, Country{"US", 2})
heap.Push(teams, Country{"Germany", 3})
heap.Push(teams, Country{"Korea", 4})
// Take the two teams with lowest score and make a new team of them
// Repeat this until there's only one team left
for teams.Len() > 1 {
t1 := heap.Pop(teams).(Scoreable)
t2 := heap.Pop(teams).(Scoreable)
heap.Push(teams, Team{t1, t2, t1.Score() + t2.Score()})
}
// Print the teams that we now have in the heap
for teams.Len() > 0 {
t := heap.Pop(teams).(Team)
fmt.Println(t)
}
}
You can find runnable code on the Go Playground.

package main
import (
"container/heap"
"fmt"
)
//Recursive data structure may looks something like
type Group struct {
Score int
Left *Group
Right *Group
Country string
}
//You can use slice to hold them organized in tree
type GrHeap []Group
//To implement your logic you can use stdlib/container/heap Heap interface
//you must implement Heap interface for your slice
func (h GrHeap) Len() int { return len(h) }
func (h GrHeap) Less(i, j int) bool { return h[i].Score < h[j].Score }
func (h GrHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *GrHeap) Push(x interface{}) {
// Push and Pop use pointer receivers because they modify the slice's length,
// not just its contents.
*h = append(*h, x.(Group))
}
func (h *GrHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}
func main() {
//you most likely already have a map
//anyway it will be handy to keep it for convenient access to individual country
score := map[string]int{
"Canada": 7,
"US": 2,
"Germany": 3,
"Korea": 4,
}
//here we allocate heap
gr := make(GrHeap, 0)
//populate it from map
for k, v := range score {
g := Group{v, nil, nil, k}
gr = append(gr, g)
}
//and initialize
heap.Init(&gr)
//and here we use heap magic to implement your logic
for len(gr) > 2 {
l := heap.Pop(&gr).(Group)
r := heap.Pop(&gr).(Group)
ng := Group{l.Score + r.Score, &l, &r, ""}
heap.Push(&gr, ng)
}
fmt.Println(gr)
fmt.Println(gr[1].Left)
fmt.Println(gr[1].Right.Left)
//and you can see it works https://play.golang.org/p/gugJxJb7rr
}

You can try map[string]interface{} with Type assertion。
Here is the demo
package main
import "fmt"
const total = "total"
func GetValue(i interface{}) int {
value, ok := i.(int)
if ok {
return value
}
return i.(map[string]interface{})[total].(int)
}
func main() {
score := map[string]interface{}{
"Canada": 7,
"US": 2,
"Germany": 3,
"Korea": 4,
}
groupCount := 0
for len(score) > 2 {
var (
firstMin = math.MaxInt32
secondMin = math.MaxInt32
firstKey = ""
secondKey = ""
)
for k, v := range score {
iv := GetValue(v)
if iv < firstMin {
secondMin = firstMin
secondKey = firstKey
firstMin = iv
firstKey = k
continue
}
if iv < secondMin {
secondMin = iv
secondKey = k
continue
}
}
groupCount++
score[fmt.Sprintf("Group%d", groupCount)] = map[string]interface{}{
firstKey: score[firstKey],
secondKey: score[secondKey],
total: GetValue(score[firstKey])+ GetValue(score[secondKey]),
}
delete(score, firstKey)
delete(score, secondKey)
}
fmt.Println(score)
}
Here is the link https://play.golang.org/p/qq5qwAsh1m

Related

A function that search for an interface{} over a slice of interface{} in Go

I'm trying to implement a function that takes an element of any type and a slice of the same type and search the first inside the second, giving it's position as result or -1 otherwise.
I'm not a Go expert, so my first thought was to pass the element to search as interface{} and the slice as []interface{}, but it didn't really work.
Here's what I tried:
package main
import (
"fmt"
)
func IsElementInListWithPos(element interface{}, list []interface{}) int {
for i := range list {
if list[i] == element {
return i
}
}
return -1
}
func main() {
list1 := []int{1, 2, 3, 4, 5, 6}
list2 := []string{"a", "b", "c", "d"}
pos1 := IsElementInListWithPos(3, list1)
pos2 := IsElementInListWithPos("a", list2)
fmt.Println(pos1, pos2)
}
It gives me the following errors:
cannot use list (type []int) as type []interface {} in argument to IsElementInListWithPos
cannot use list2 (type []string) as type []interface {} in argument to IsElementInListWithPos
Any idea how I could solve this issue without actually using two different functions?
Thanks in advance.
The sort package demonstrates how interfaces can be used to implement algorithms in a type-independent way.
Linear search requires two essential operations that depend on the haystack element type, Len and Equal. So we can write the following Haystack interface and a Search function that using it:
type Haystack interface {
Len() int
Equal(int, interface{}) bool
}
func Search(haystack Haystack, needle interface{}) int {
for i := 0; i < haystack.Len(); i++ {
if haystack.Equal(i, needle) {
return i
}
}
return -1
}
This makes writing implementations for Haystack simple, but not type-safe:
type Strings []string
func (s Strings) Len() int { return len(s) }
func (s Strings) Equal(i int, x interface{}) bool { return s[i] == x.(string) }
type Ints []int
func (s Ints) Len() int { return len(s) }
func (s Ints) Equal(i int, x interface{}) bool { return s[i] == x.(int) }
func main() {
strings := []string{"b", "a", "c", "d"}
fmt.Println(Search(Strings(strings), "c")) // 2
fmt.Println(Search(Strings(strings), "e")) // -1
ints := []int{2, 1, 3, 4}
fmt.Println(Search(Ints(ints), 3)) // 2
fmt.Println(Search(Ints(ints), 5)) // -1
}
Note the type assertions in the Equal methods. To make this type-safe we have to get rid of the interface{} argument to Equal:
type Haystack interface {
Len() int
Equal(int) bool
}
func Search(haystack Haystack) int {
for i := 0; i < haystack.Len(); i++ {
if haystack.Equal(i) {
return i
}
}
return -1
}
type Strings struct {
hs []string
needle string
}
func (s Strings) Len() int { return len(s.hs) }
func (s Strings) Equal(i int) bool { return s.hs[i] == s.needle }
type Ints struct {
hs []int
needle int
}
func (s Ints) Len() int { return len(s.hs) }
func (s Ints) Equal(i int) bool { return s.hs[i] == s.needle }
func main() {
strings := []string{"b", "a", "c", "d"}
fmt.Println(Search(Strings{strings, "c"})) // 2
fmt.Println(Search(Strings{strings, "e"})) // -1
ints := []int{2, 1, 3, 4}
fmt.Println(Search(Ints{ints, 3})) // 2
fmt.Println(Search(Ints{ints, 5})) // -1
}
This made both the interface implementations and using the Search function much more complicated.
The moral of the story is that using interfaces this way requires a sufficiently complicated algorithm to be worth the trouble. If writing the interface implementation for a particular type is more work than writing the concrete implementation for the algorithm, well, then just write the concrete functions you need:
func SearchStr(haystack []string, needle string) int {
for i, x := range haystack {
if x == needle {
return i
}
}
return -1
}
func SearchInt(haystack []int, needle int) int {
for i, x := range haystack {
if x == needle {
return i
}
}
return -1
}
func main() {
strings := []string{"b", "a", "c", "d"}
fmt.Println(SearchStr(strings, "c")) // 2
fmt.Println(SearchStr(strings, "e")) // -1
ints := []int{2, 1, 3, 4}
fmt.Println(SearchInt(ints, 3)) // 2
fmt.Println(SearchInt(ints, 5)) // -1
}
Currently, it is not possible to build a solution that respects all your criteria. It will be possible once generics are implemented. Or you could try building one using reflect, but that will yield a complex and potentially slow solution... so I generally advise against using reflect for something as simple as this (see second snippet below).
What you can do right now is to use something like:
func FindFirst(n int, f func(int) bool) int {
for i := 0; i < n; i++ {
if f(i) {
return i
}
}
return -1
}
// in your code (s is the slice, e the value you are searching for)
i := FindFirst(len(s), func(i int) bool {
return s[i] == e
})
if i != -1 {
// i is the index of the element with value e
}
This, as you can imagine, does not make much sense... as it's arguably simpler, faster, and more idiomatic to simply write out the loop explicitly:
// in your code (s is the slice, e the value you are searching for)
for i, v := range s {
if v == e {
_ = i // i is the index of the element with value e
break
}
}
Obviously, this whole approach (linear scan) is only reasonable if the number of elements in the slice is small. If your slice is big and changes rarely, it would arguably make more sense (from a time complexity perspective) to sort it (sort.Slice) first and then do binary searches (sort.Search) on the sorted slice. Or, alternatively, you could use a map instead: in which case (assuming keys are small) lookup would be O(1).

Sort on map on value (attribute of Struct)

I have the below map:
detail := make(map[string]*Log)
type Log struct {
Id []string
Name []string
Priority int // value could be 1, 2, 3
Message string
}
I want to sort the "detail" map on basis of the value which is a struct in my case. This should be sorted by attribute "Priority".
For example, Log (map of struct) may have values similar to below:
Z : &{[ba60] [XYZ] 3 "I am the boss"}
B : &{[ca50] [ABC] 2 "I am the Junior"}
U : &{[zc20] [PQR] 1 "I am the Newbie"}
I want them to print from increasing Priority order i.e. 1 to 3
U : &{[zc20] [PQR] 1 "I am the Newbie"}
B : &{[ca50] [ABC] 2 "I am the Junior"}
Z : &{[ba60] [XYZ] 3 "I am the boss"}
I tried to use the sort and implemented the Sort interface, but seems like still missing the clue somewhere. So, I implemented the below interface:
type byPriority []*Log
func (d byPriority) Len() int {
return len(d)
}
func (d byPriority) Less(i, j int) bool {
return d[i].Priority < d[j].Priority
}
func (d byPriority) Swap(i, j int) {
d[i], d[j] = d[j], d[i]
}
But how should I apply sort.Sort() method on this map to get the sorted result. Do I need to add some more code?
The map type in Go is unordered. Regardless of what you do to a map, the next time you iterate over it you will receive the keys in random order. Thus there is no way to "sort" a map.
What you can do is copy the entries of the map into a slice, which is sortable.
package main
import (
"fmt"
"sort"
)
type Log struct {
Id []string
Name []string
Priority int // value could be 1, 2, 3
Message string
}
type Entry struct {
key string
value *Log
}
type byPriority []Entry
func (d byPriority) Len() int {
return len(d)
}
func (d byPriority) Less(i, j int) bool {
return d[i].value.Priority < d[j].value.Priority
}
func (d byPriority) Swap(i, j int) {
d[i], d[j] = d[j], d[i]
}
func printSorted(detail map[string]*Log) {
// Copy entries into a slice.
slice := make(byPriority, 0, len(detail))
for key, value := range detail {
slice = append(slice, Entry{key, value})
}
// Sort the slice.
sort.Sort(slice)
// Iterate and print the entries in sorted order.
for _, entry := range slice {
fmt.Printf("%s : %v\n", entry.key, entry.value)
}
}
func main() {
detail := map[string]*Log{
"Z": &Log{[]string{"ba60"}, []string{"XYZ"}, 3, "I am the boss"},
"B": &Log{[]string{"ca50"}, []string{"ABC"}, 2, "I am the Junior"},
"U": &Log{[]string{"zc20"}, []string{"PQR"}, 1, "I am the Newbie"},
}
printSorted(detail)
}

In Go, when using multiple return statements, how do you invoke each specific one?

I am attempting to have two variations of the sort method: one form which sorts the elements by name, and another that sorts the elements by salary. sort.Sort(people(data)) works when my less method compares whatever.salary. It also works if I change it to whatever.name. I would like to be able to specifically call both of these options in the less method as shown in the below code. My logic is using sort.Sort(people(data.name)) for name, and sort.Sort(people(data.salary)) for salary. These are not working. Can this even be done?
package main
import (
"fmt"
"sort"
)
type Comparable interface {
Len()
Less(i, j int) bool
Swap(i, j int)
}
type person struct {
name string
salary float64
}
func (a person) String() string {
return fmt.Sprintf("%s: %g \n", a.name, a.salary)
}
type people []*person
func (a people) Len() int {
return len(a)
}
func (a people) Less(i, j int) bool {
return a[i].salary < a[j].salary
return a[i].name < a[j].name
}
func (a people) Swap(i, j int) {
a[i], a[j] = a[j], a[i]
}
func main() {
var data = make(people, 10)
var a, b, c, d, e, f, g, h, i, j person
a.name, b.name, c.name, d.name, e.name, f.name,
g.name, h.name, i.name, j.name = "Sheila Broflovski", "Ben Affleck",
"Mr. Hankey", "Stan Marsh", "Kyle Broflovski", "Eric Cartman",
"Kenny McCormick", "Mr. Garrison", "Matt Stone", "Trey Parker"
a.salary, b.salary, c.salary, d.salary, e.salary, f.salary,
g.salary, h.salary, i.salary, j.salary = 82000, 74000, 0, 400,
2500, 1000, 4, 34000, 234000, 234000
a.salary = 82000
data[0] = &a
data[1] = &b
data[2] = &c
data[3] = &d
data[4] = &e
data[5] = &f
data[6] = &g
data[7] = &h
data[8] = &i
data[9] = &j
fmt.Println("\n\n\n")
fmt.Print(data)
sort.Sort(people(data)) //This works even with the two return statements
sort.Sort(people(data.name)) //This does not work. Exist, a version that does?
sort.Sort(people(data.salary)) //This does not work. Exist, a version that does?
fmt.Println("\n\n\n")
fmt.Print(data)
}
A common way to introduce the methods for sorting is to use a new type that describes the sorting condition. That's byName and bySalary here. Then you can sort using sort.Sort(byName(data)).
Here's some demonstration code. Go also has really great good for constructing data-structures (used prolifically, for example, in table-driven tests), which also can help construct your people data here.
package main
import "fmt"
import "sort"
type person struct {
Name string
Salary float64
}
type people []*person
type byName people
type bySalary people
func (p byName) Len() int { return len(p) }
func (p byName) Less(i, j int) bool { return p[i].Name < p[j].Name }
func (p byName) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p bySalary) Len() int { return len(p) }
func (p bySalary) Less(i, j int) bool { return p[i].Salary < p[j].Salary }
func (p bySalary) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func main() {
p := people{
{"Sheila Broflovski", 82000},
{"Ben Affleck", 74000},
{"Mr. Hankey", 0},
{"Stan Marsh", 400},
{"Kyle Broflovski", 2500},
{"Eric Cartman", 1000},
{"Kenny McCormick", 4},
{"Mr. Garrison", 34000},
{"Matt Stone", 234000},
{"Trey Parker", 234000},
}
fmt.Println("by name")
sort.Sort(byName(p))
for _, x := range p {
fmt.Println(*x)
}
fmt.Println("by salary")
sort.Sort(bySalary(p))
for _, x := range p {
fmt.Println(*x)
}
}
To implement a second sort ordering with the standard library sort package, you will need to define a helper type that implements sort.Interface. To handle the salary case in your example, you could do something like the following:
type bySalary struct {
people
}
func (s bySalary) Less(i, j int) bool {
return s.people[i].salary < s.people[j].salary
}
Here, the Len and Swap methods required by sort.Interface come from the embedded people slice, while I've provided a replacement comparison operation. To sort a people array, you can now call:
sort.Sort(bySalary{data})
Using this pattern, it is easy to implement as many additional sort keys as you need with very little duplicate code.
You can play with this example here: http://play.golang.org/p/kq3SuXMylT

Sorting a Map of Structs - GOLANG

I have a map of structs that I am populating by streaming data to a Go program. The way the map is updated is similar to the example below.
Once I have this map of structs populated, what is the best (or good) way to sort this map by the values of the count field in the struct?
package main
type data struct {
count int64
}
func main() {
m := make(map[string]data)
m["x"] = data{0, 0}
if xx, ok := m["x"]; ok {
xx.count = 2
m["x"] = xx
} else {
panic("X isn't in the map")
}
}
This example can be run here: http://play.golang.org/p/OawL6QIXuO
As siritinga already pointed out, the elements of a map isn't ordered, so you cannot sort it.
What you can do is to create a slice and sort the elements using the sort package:
package main
import (
"fmt"
"sort"
)
type dataSlice []*data
type data struct {
count int64
size int64
}
// Len is part of sort.Interface.
func (d dataSlice) Len() int {
return len(d)
}
// Swap is part of sort.Interface.
func (d dataSlice) Swap(i, j int) {
d[i], d[j] = d[j], d[i]
}
// Less is part of sort.Interface. We use count as the value to sort by
func (d dataSlice) Less(i, j int) bool {
return d[i].count < d[j].count
}
func main() {
m := map[string]*data {
"x": {0, 0},
"y": {2, 9},
"z": {1, 7},
}
s := make(dataSlice, 0, len(m))
for _, d := range m {
s = append(s, d)
}
// We just add 3 to one of our structs
d := m["x"]
d.count += 3
sort.Sort(s)
for _, d := range s {
fmt.Printf("%+v\n", *d)
}
}
Output:
{count:1 size:7}
{count:2 size:9}
{count:3 size:0}
Playground
Edit
Updated the example to use pointers and to include a map so that you can both do lookups and have a slice to sort over.

Go: What is the fastest/cleanest way to remove multiple entries from a slice?

How would you implement the deleteRecords function in the code below:
Example:
type Record struct {
id int
name string
}
type RecordList []*Record
func deleteRecords( l *RecordList, ids []int ) {
// Assume the RecordList can contain several 100 entries.
// and the number of the of the records to be removed is about 10.
// What is the fastest and cleanest ways to remove the records that match
// the id specified in the records list.
}
I did some micro-benchmarking on my machine, trying out most of the approaches given in the replies here, and this code comes out fastest when you've got up to about 40 elements in the ids list:
func deleteRecords(data []*Record, ids []int) []*Record {
w := 0 // write index
loop:
for _, x := range data {
for _, id := range ids {
if id == x.id {
continue loop
}
}
data[w] = x
w++
}
return data[:w]
}
You didn't say whether it's important to preserve the order of records in the list. If you don't then this function is faster than the above and still fairly clean.
func reorder(data []*Record, ids []int) []*Record {
n := len(data)
i := 0
loop:
for i < n {
r := data[i]
for _, id := range ids {
if id == r.id {
data[i] = data[n-1]
n--
continue loop
}
}
i++
}
return data[0:n]
}
As the number of ids rises, so does the cost of the linear search. At around 50 elements, using a map or doing a binary search to look up the id becomes more efficient, as long as you can avoid rebuilding the map (or resorting the list) every time. At several hundred ids, it becomes more efficient to use a map or a binary search even if you have to rebuild it every time.
If you wish to preserve original contents of the slice, something like this is more appropriate:
func deletePreserve(data []*Record, ids []int) []*Record {
wdata := make([]*Record, len(data))
w := 0
loop:
for _, x := range data {
for _, id := range ids {
if id == x.id {
continue loop
}
}
wdata[w] = x
w++
}
return wdata[0:w]
}
For a personal project, I did something like this:
func filter(sl []int, fn func(int) bool) []int {
result := make([]int, 0, len(sl))
last := 0
for i, v := range sl {
if fn(v) {
result = append(result, sl[last:i]...)
last = i + 1
}
}
return append(result, sl[last:]...)
}
It doesn't mutate the original, but should be relatively efficient.
It's probably better to just do:
func filter(sl []int, fn func(int) bool) (result []int) {
for _, v := range sl {
if !fn(v) {
result = append(result, v)
}
}
return
}
Simpler and cleaner.
If you want to do it in-place, you probably want something like:
func filter(sl []int, fn func(int) bool) []int {
outi := 0
res := sl
for _, v := range sl {
if !fn(v) {
res[outi] = v
outi++
}
}
return res[0:outi]
}
You can optimize this to use copy to copy ranges of elements, but that's twice
the code and probably not worth it.
So, in this specific case, I'd probably do something like:
func deleteRecords(l []*Record, ids []int) []*Record {
outi := 0
L:
for _, v := range l {
for _, id := range ids {
if v.id == id {
continue L
}
}
l[outi] = v
outi++
}
return l[0:outi]
}
(Note: untested.)
No allocations, nothing fancy, and assuming the rough size of the list of Records and the list of ids you presented, a simple linear search is likely to do as well as fancier things but without any overhead. I realize that my version mutates the slice and returns a new slice, but that's not un-idiomatic in Go, and it avoids forcing the slice at the callsite to be heap allocated.
For the case you described, where len(ids) is approximately 10 and len(*l) is in the several hundreds, this should be relatively fast, since it minimizes memory allocations by updating in place.
package main
import (
"fmt"
"strconv"
)
type Record struct {
id int
name string
}
type RecordList []*Record
func deleteRecords(l *RecordList, ids []int) {
rl := *l
for i := 0; i < len(rl); i++ {
rid := rl[i].id
for j := 0; j < len(ids); j++ {
if rid == ids[j] {
copy(rl[i:len(*l)-1], rl[i+1:])
rl[len(rl)-1] = nil
rl = rl[:len(rl)-1]
break
}
}
}
*l = rl
}
func main() {
l := make(RecordList, 777)
for i := range l {
l[i] = &Record{int(i), "name #" + strconv.Itoa(i)}
}
ids := []int{0, 1, 2, 4, 8, len(l) - 1, len(l)}
fmt.Println(ids, len(l), cap(l), *l[0], *l[1], *l[len(l)-1])
deleteRecords(&l, ids)
fmt.Println(ids, len(l), cap(l), *l[0], *l[1], *l[len(l)-1])
}
Output:
[0 1 2 4 8 776 777] 777 777 {0 name #0} {1 name #1} {776 name #776}
[0 1 2 4 8 776 777] 772 777 {1 name #1} {3 name #3} {775 name #775}
Instead of repeatedly searching ids, you could use a map. This code preallocates the full size of the map, and then just moves array elements in place. There are no other allocations.
func deleteRecords(l *RecordList, ids []int) {
m := make(map[int]bool, len(ids))
for _, id := range ids {
m[id] = true
}
s, x := *l, 0
for _, r := range s {
if !m[r.id] {
s[x] = r
x++
}
}
*l = s[0:x]
}
Use the vector package's Delete method as a guide, or just use a Vector instead of a slice.
Here is one option but I would hope there are cleaner/faster more functional looking ones:
func deleteRecords( l *RecordList, ids []int ) *RecordList {
var newList RecordList
for _, rec := range l {
toRemove := false
for _, id := range ids {
if rec.id == id {
toRemove = true
}
if !toRemove {
newList = append(newList, rec)
}
}
return newList
}
With large enough l and ids it will be more effective to Sort() both lists first and then do a single loop over them instead of two nested loops

Resources