├── .gitignore ├── Makefile ├── Readme.md ├── go.mod ├── main.go ├── linkedlist ├── linked_list_test.go └── linked_list.go ├── ordereddict ├── ordered_dict_test.go └── ordered_dict.go ├── defaultdict ├── defaultdict.go └── defaultdict_test.go ├── LICENSE ├── counter ├── counter_test.go └── counter.go └── go.sum /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | go test -v -race -cover ./... 3 | 4 | go get -u honnef.co/go/tools/cmd/staticcheck 5 | staticcheck ./... 6 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # go-playground 2 | 3 | Sometimes I like to try out new code, or rewrite algorithms when learning a new language or just for practice. This repository is where I put that code :) 4 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/tizz98/go-playground 2 | 3 | require ( 4 | github.com/BurntSushi/toml v0.3.1 // indirect 5 | github.com/saibing/bingo v1.1.0 // indirect 6 | github.com/stretchr/testify v1.3.0 7 | honnef.co/go/tools v0.0.0-20190128043916-71123fcbb8fe // indirect 8 | ) 9 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/tizz98/go-playground/counter" 7 | "github.com/tizz98/go-playground/defaultdict" 8 | "github.com/tizz98/go-playground/ordereddict" 9 | ) 10 | 11 | func main() { 12 | d := ordereddict.New() 13 | d.Set("foo", "bar") 14 | d.Set("baz", 123) 15 | 16 | for v := range d.Iterate() { 17 | fmt.Println(v) 18 | } 19 | 20 | c := counter.New() 21 | c.AddItems("foo", "foo", "bar", "baz") 22 | 23 | for i, item := range c.MostCommon(5) { 24 | fmt.Printf("%d: %#v seen %d time(s)\n", i+1, item.Value, item.Count) 25 | } 26 | 27 | dict := defaultdict.New(defaultdict.IntDefault) 28 | fmt.Printf("foo default: %d\n", dict.Get("foo")) 29 | } 30 | -------------------------------------------------------------------------------- /linkedlist/linked_list_test.go: -------------------------------------------------------------------------------- 1 | package linkedlist 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestLinkedList(t *testing.T) { 10 | t.Run("Iteration", func(t *testing.T) { 11 | l := New() 12 | l.Append("foo") 13 | l.Append("Bar") 14 | 15 | i := 0 16 | expected := []string{"foo", "Bar"} 17 | 18 | for n := range l.Iterate() { 19 | assert.Equal(t, n.value.(string), expected[i]) 20 | i++ 21 | } 22 | }) 23 | 24 | t.Run("Removal", func(t *testing.T) { 25 | l := New() 26 | l.Append("foo") 27 | bar := l.Append("Bar") 28 | l.Append("baz") 29 | l.Append("abc") 30 | 31 | assert.True(t, l.Remove(bar)) 32 | 33 | i := 0 34 | expected := []string{"foo", "baz", "abc"} 35 | 36 | for n := range l.Iterate() { 37 | assert.Equal(t, n.value.(string), expected[i]) 38 | i++ 39 | } 40 | }) 41 | } 42 | 43 | -------------------------------------------------------------------------------- /ordereddict/ordered_dict_test.go: -------------------------------------------------------------------------------- 1 | package ordereddict 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestOrderedDict(t *testing.T) { 10 | d := New() 11 | d.Set("foo", "bar") 12 | d.Set("test", 123) 13 | 14 | assert.Equal(t, d.Get("foo"), "bar") 15 | assert.Equal(t, d.Get("test"), 123) 16 | 17 | assert.True(t, d.Remove("test")) 18 | assert.False(t, d.Remove("test")) 19 | 20 | d.Set("test", 123) 21 | d.Set("baz", "baz") 22 | d.Set("test", 456) 23 | assert.Equal(t, d.Get("test"), 456) 24 | 25 | // because we call .Set("test", ...) again, it should be moved 26 | // to the back of the list 27 | t.Run("Iterate", func(t *testing.T) { 28 | i := 0 29 | expected := []interface{}{"bar", "baz", 456} 30 | 31 | for v := range d.Iterate() { 32 | assert.Equal(t, expected[i], v) 33 | i++ 34 | } 35 | }) 36 | } 37 | 38 | -------------------------------------------------------------------------------- /defaultdict/defaultdict.go: -------------------------------------------------------------------------------- 1 | package defaultdict 2 | 3 | type DefaultDict struct { 4 | mapping map[interface{}]interface{} 5 | defaultGetter func() interface{} 6 | } 7 | 8 | func New(defaultGetter func() interface{}) *DefaultDict { 9 | return &DefaultDict{ 10 | mapping: map[interface{}]interface{}{}, 11 | defaultGetter: defaultGetter, 12 | } 13 | } 14 | 15 | func (d *DefaultDict) Get(key interface{}) interface{} { 16 | if value, ok := d.mapping[key]; ok { 17 | return value 18 | } 19 | return d.defaultGetter() 20 | } 21 | 22 | func (d *DefaultDict) Update(key interface{}, fn func(value interface{}) interface{}) { 23 | value, ok := d.mapping[key] 24 | if !ok { 25 | value = d.defaultGetter() 26 | } 27 | d.mapping[key] = fn(value) 28 | } 29 | 30 | func (d *DefaultDict) Set(key, value interface{}) { 31 | d.mapping[key] = value 32 | } 33 | 34 | func IntDefault() interface{} { return int(0) } 35 | func Int32Default() interface{} { return int32(0) } 36 | func Int64Default() interface{} { return int64(0) } 37 | 38 | func Float64Default() interface{} { return float64(0) } 39 | func Float32Default() interface{} { return float32(0) } 40 | -------------------------------------------------------------------------------- /ordereddict/ordered_dict.go: -------------------------------------------------------------------------------- 1 | package ordereddict 2 | 3 | import "github.com/tizz98/go-playground/linkedlist" 4 | 5 | type OrderedDict struct { 6 | lookup map[string]*linkedlist.LinkedListNode 7 | list *linkedlist.LinkedList 8 | } 9 | 10 | func New() *OrderedDict { 11 | return &OrderedDict{ 12 | lookup: make(map[string]*linkedlist.LinkedListNode), 13 | list: linkedlist.New(), 14 | } 15 | } 16 | 17 | func (d *OrderedDict) Set(key string, value interface{}) { 18 | if n, ok := d.lookup[key]; ok { 19 | d.list.Remove(n) 20 | } 21 | 22 | d.lookup[key] = d.list.Append(value) 23 | } 24 | 25 | func (d *OrderedDict) Get(key string) interface{} { 26 | return d.lookup[key].Value() 27 | } 28 | 29 | func (d *OrderedDict) Remove(key string) bool { 30 | if n, ok := d.lookup[key]; ok { 31 | if ok := d.list.Remove(n); !ok { 32 | return false 33 | } 34 | delete(d.lookup, key) 35 | return true 36 | } 37 | return false 38 | } 39 | 40 | func (d *OrderedDict) Iterate() chan interface{} { 41 | ch := make(chan interface{}) 42 | 43 | go func() { 44 | for v := range d.list.Iterate() { 45 | ch <- v.Value() 46 | } 47 | 48 | close(ch) 49 | }() 50 | 51 | return ch 52 | } 53 | 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /linkedlist/linked_list.go: -------------------------------------------------------------------------------- 1 | package linkedlist 2 | 3 | type LinkedListNode struct { 4 | next *LinkedListNode 5 | prev *LinkedListNode 6 | 7 | value interface{} 8 | } 9 | 10 | func (n *LinkedListNode) Value() interface{} { 11 | return n.value 12 | } 13 | 14 | type LinkedList struct { 15 | head *LinkedListNode 16 | tail *LinkedListNode 17 | } 18 | 19 | func New() *LinkedList { 20 | list := &LinkedList{ 21 | head: &LinkedListNode{}, 22 | tail: &LinkedListNode{}, 23 | } 24 | list.head.next = list.tail 25 | list.tail.prev = list.head 26 | return list 27 | } 28 | 29 | func (l *LinkedList) Append(value interface{}) *LinkedListNode { 30 | n := &LinkedListNode{ 31 | prev: l.tail.prev, 32 | next: l.tail, 33 | value: value, 34 | } 35 | 36 | l.tail.prev.next = n 37 | l.tail.prev = n 38 | 39 | return n 40 | } 41 | 42 | func (l *LinkedList) Remove(n *LinkedListNode) bool { 43 | if n == l.head || n == l.tail { 44 | return false 45 | } 46 | 47 | n.prev.next = n.next 48 | n.next.prev = n.prev 49 | 50 | return true 51 | } 52 | 53 | func (l *LinkedList) Iterate() chan *LinkedListNode { 54 | ch := make(chan *LinkedListNode) 55 | 56 | go func() { 57 | n := l.head 58 | 59 | for n.next != l.tail { 60 | ch <- n.next 61 | n = n.next 62 | } 63 | 64 | close(ch) 65 | }() 66 | 67 | return ch 68 | } 69 | 70 | -------------------------------------------------------------------------------- /counter/counter_test.go: -------------------------------------------------------------------------------- 1 | package counter 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestCounter_MostCommon(t *testing.T) { 10 | t.Run("NoValues", func(t *testing.T) { 11 | counter := New() 12 | assert.Empty(t, counter.MostCommon(5)) 13 | }) 14 | 15 | t.Run("NGreaterThanValues", func(t *testing.T) { 16 | counter := New() 17 | counter.AddItems("foo", "foo", "foo", "baz", "bar", "bar") 18 | assert.Equal(t, []CounterItem{ 19 | {"foo", 3}, 20 | {"bar", 2}, 21 | {"baz", 1}, 22 | }, counter.MostCommon(5)) 23 | }) 24 | 25 | t.Run("NLessThanValues", func(t *testing.T) { 26 | counter := New() 27 | counter.AddItems("foo", "foo", "foo", "baz", "bar", "bar") 28 | assert.Equal(t, []CounterItem{ 29 | {"foo", 3}, 30 | }, counter.MostCommon(1)) 31 | }) 32 | 33 | t.Run("NEqualToValues", func(t *testing.T) { 34 | counter := New() 35 | counter.AddItems("foo", "foo", "foo", "baz", "bar", "bar") 36 | assert.Equal(t, []CounterItem{ 37 | {"foo", 3}, 38 | {"bar", 2}, 39 | {"baz", 1}, 40 | }, counter.MostCommon(3)) 41 | }) 42 | 43 | t.Run("ValuesExistAfterGetting", func(t *testing.T) { 44 | counter := New() 45 | counter.AddItems("foo", "foo", "foo", "baz", "bar", "bar") 46 | assert.Equal(t, []CounterItem{ 47 | {"foo", 3}, 48 | {"bar", 2}, 49 | {"baz", 1}, 50 | }, counter.MostCommon(3)) 51 | 52 | assert.Equal(t, []CounterItem{ 53 | {"foo", 3}, 54 | {"bar", 2}, 55 | {"baz", 1}, 56 | }, counter.MostCommon(3)) 57 | 58 | assert.Equal(t, 3, counter.Get("foo")) 59 | assert.Equal(t, 2, counter.Get("bar")) 60 | assert.Equal(t, 1, counter.Get("baz")) 61 | }) 62 | } 63 | 64 | func TestCounter_Get(t *testing.T) { 65 | t.Run("NonExistentItem", func(t *testing.T) { 66 | counter := New() 67 | assert.Equal(t, 0, counter.Get("foo")) 68 | }) 69 | 70 | t.Run("ExistingItem", func(t *testing.T) { 71 | counter := New() 72 | counter.AddItems("foo", "bar", "foo") 73 | assert.Equal(t, 2, counter.Get("foo")) 74 | assert.Equal(t, 1, counter.Get("bar")) 75 | }) 76 | } 77 | 78 | -------------------------------------------------------------------------------- /defaultdict/defaultdict_test.go: -------------------------------------------------------------------------------- 1 | package defaultdict 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestDefaultDict(t *testing.T) { 10 | t.Run("Int", func(t *testing.T) { 11 | d := New(IntDefault) 12 | d.Update("foo", func(value interface{}) interface{} { 13 | return value.(int) + 1 14 | }) 15 | d.Update("foo", func(value interface{}) interface{} { 16 | return value.(int) + 1 17 | }) 18 | 19 | assert.Equal(t, 2, d.Get("foo").(int)) 20 | }) 21 | 22 | t.Run("Int32", func(t *testing.T) { 23 | d := New(Int32Default) 24 | d.Update("foo", func(value interface{}) interface{} { 25 | return value.(int32) + 1 26 | }) 27 | d.Update("foo", func(value interface{}) interface{} { 28 | return value.(int32) + 1 29 | }) 30 | 31 | assert.Equal(t, int32(2), d.Get("foo").(int32)) 32 | }) 33 | 34 | t.Run("Int64", func(t *testing.T) { 35 | d := New(Int64Default) 36 | d.Update("foo", func(value interface{}) interface{} { 37 | return value.(int64) + 1 38 | }) 39 | d.Update("foo", func(value interface{}) interface{} { 40 | return value.(int64) + 1 41 | }) 42 | 43 | assert.Equal(t, int64(2), d.Get("foo").(int64)) 44 | }) 45 | 46 | t.Run("Float32", func(t *testing.T) { 47 | d := New(Float32Default) 48 | d.Update("foo", func(value interface{}) interface{} { 49 | return value.(float32) + 1 50 | }) 51 | d.Update("foo", func(value interface{}) interface{} { 52 | return value.(float32) + 1 53 | }) 54 | 55 | assert.Equal(t, float32(2), d.Get("foo").(float32)) 56 | }) 57 | 58 | t.Run("Float64", func(t *testing.T) { 59 | d := New(Float64Default) 60 | d.Update("foo", func(value interface{}) interface{} { 61 | return value.(float64) + 1 62 | }) 63 | d.Update("foo", func(value interface{}) interface{} { 64 | return value.(float64) + 1 65 | }) 66 | 67 | assert.Equal(t, float64(2), d.Get("foo").(float64)) 68 | }) 69 | 70 | t.Run("SetUpdateGet", func(t *testing.T) { 71 | d := New(IntDefault) 72 | d.Set("foo", 100) 73 | d.Set("bar", 10) 74 | 75 | assert.Equal(t, 100, d.Get("foo")) 76 | assert.Equal(t, 10, d.Get("bar")) 77 | 78 | d.Update("bar", func(value interface{}) interface{} { 79 | return 900 80 | }) 81 | 82 | assert.Equal(t, 900, d.Get("bar")) 83 | }) 84 | } 85 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 4 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 6 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 7 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 8 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 9 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 10 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 11 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 12 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 13 | github.com/saibing/bingo v1.1.0 h1:iglyFTYe6/ZxaICp499bmAk30Uq92oFa6K7kgz8Spxw= 14 | github.com/saibing/bingo v1.1.0/go.mod h1:kPUuE8jwIRE8KldxfQN6kzUxAZRo1pzuXU/EBbQR5NU= 15 | github.com/slimsag/godocmd v0.0.0-20161025000126-a1005ad29fe3 h1:sAARUcYbwxnebBeWHzKX2MeyXtzy25TEglCTz9BhueY= 16 | github.com/slimsag/godocmd v0.0.0-20161025000126-a1005ad29fe3/go.mod h1:AIBPxLCkKUFc2ZkjCXzs/Kk9OUhQLw/Zicdd0Rhqz2U= 17 | github.com/sourcegraph/go-lsp v0.0.0-20181119182933-0c7d621186c1/go.mod h1:tpps84QRlOVVLYk5QpKYX8Tr289D1v/UTWDLqeguiqM= 18 | github.com/sourcegraph/jsonrpc2 v0.0.0-20180831160525-549eb959f029 h1:86fjxPjv7RGdGho8ShBSEDZQPERPNnZbny8fJ0FcOGo= 19 | github.com/sourcegraph/jsonrpc2 v0.0.0-20180831160525-549eb959f029/go.mod h1:eESpbCslcLDs8j2D7IEdGVgul7xuk9odqDTaor30IUU= 20 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 21 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 22 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 23 | golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b h1:MQE+LT/ABUuuvEZ+YQAMSXindAdUh7slEmAkup74op4= 24 | golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 25 | golang.org/x/tools v0.0.0-20181122213734-04b5d21e00f1 h1:bsEj/LXbv3BCtkp/rBj9Wi/0Nde4OMaraIZpndHAhdI= 26 | golang.org/x/tools v0.0.0-20181122213734-04b5d21e00f1/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 27 | gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= 28 | honnef.co/go/tools v0.0.0-20190109154334-5bcec433c8ea h1:Mqe+pbbXrgs4O/B8PTc7PMpbZ1YCzT0YAz8FHqE8AcM= 29 | honnef.co/go/tools v0.0.0-20190109154334-5bcec433c8ea/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 30 | honnef.co/go/tools v0.0.0-20190128043916-71123fcbb8fe h1:/GZ/onp6W295MEgrIwtlbnxmFSKGavFp7/D7tMVyuaM= 31 | honnef.co/go/tools v0.0.0-20190128043916-71123fcbb8fe/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 32 | -------------------------------------------------------------------------------- /counter/counter.go: -------------------------------------------------------------------------------- 1 | package counter 2 | 3 | import ( 4 | "container/heap" 5 | "sync" 6 | ) 7 | 8 | type counterItem struct { 9 | value interface{} 10 | // the number of times this item is in the counter. 11 | // since we're using a heap, this can be thought of as the priority 12 | count int 13 | // the index in the heap array 14 | index int 15 | } 16 | 17 | // A "max heap", where the largest item is at the "top" of the heap 18 | type counterHeap []*counterItem 19 | 20 | func (h counterHeap) Len() int { 21 | return len(h) 22 | } 23 | 24 | func (h counterHeap) Less(i, j int) bool { 25 | // We want Pop to give us the highest, not lowest, so we use greater than here 26 | return h[i].count > h[j].count 27 | } 28 | 29 | func (h counterHeap) Swap(i, j int) { 30 | h[i], h[j] = h[j], h[i] 31 | h[i].index = i 32 | h[j].index = j 33 | } 34 | 35 | func (h *counterHeap) Push(x interface{}) { 36 | n := len(*h) 37 | item := x.(*counterItem) 38 | item.index = n 39 | *h = append(*h, item) 40 | } 41 | 42 | func (h *counterHeap) Pop() interface{} { 43 | old := *h 44 | n := len(old) 45 | item := old[n-1] 46 | item.index = -1 47 | *h = old[0 : n-1] 48 | return item 49 | } 50 | 51 | func (h *counterHeap) update(item *counterItem, value interface{}, count int) { 52 | item.value = value 53 | item.count = count 54 | heap.Fix(h, item.index) 55 | } 56 | 57 | type Counter struct { 58 | mapping map[interface{}]*counterItem 59 | heap counterHeap 60 | mutex sync.Mutex 61 | } 62 | 63 | func New() *Counter { 64 | return &Counter{ 65 | mapping: map[interface{}]*counterItem{}, 66 | heap: make(counterHeap, 0), 67 | } 68 | } 69 | 70 | type CounterItem struct { 71 | Value interface{} 72 | Count int 73 | } 74 | 75 | // TODO: is it possible to get the most common without popping and pushing? 76 | // O(n log k) 77 | func (c *Counter) MostCommon(n int) (items []CounterItem) { 78 | c.mutex.Lock() 79 | defer c.mutex.Unlock() 80 | 81 | var toPushBack []*counterItem 82 | 83 | for n > 0 && len(c.heap) > 0 { 84 | item := heap.Pop(&c.heap).(*counterItem) 85 | items = append(items, CounterItem{ 86 | Value: item.value, 87 | Count: item.count, 88 | }) 89 | toPushBack = append(toPushBack, item) 90 | n-- 91 | } 92 | 93 | for _, item := range toPushBack { 94 | heap.Push(&c.heap, item) 95 | } 96 | 97 | return 98 | } 99 | 100 | func (c *Counter) Get(item interface{}) int { 101 | if counterItem, ok := c.mapping[item]; ok { 102 | return counterItem.count 103 | } 104 | return 0 105 | } 106 | 107 | func (c *Counter) AddItems(items ...interface{}) { 108 | for _, item := range items { 109 | c.addItem(item) 110 | } 111 | } 112 | 113 | func (c *Counter) addItem(item interface{}) { 114 | c.mutex.Lock() 115 | defer c.mutex.Unlock() 116 | 117 | if cItem, ok := c.mapping[item]; ok { 118 | c.heap.update(cItem, cItem.value, cItem.count+1) 119 | } else { 120 | cItem := &counterItem{ 121 | value: item, 122 | count: 1, 123 | } 124 | c.mapping[item] = cItem 125 | heap.Push(&c.heap, cItem) 126 | } 127 | } 128 | 129 | --------------------------------------------------------------------------------