package mapz import "sync" // CountingMultiMap is a multimap that counts the number of distinct values for each // key, removing the key from the map when the count reaches zero. Safe for concurrent // use. type CountingMultiMap[T comparable, Q comparable] struct { valuesByKey map[T]*Set[Q] // GUARDED_BY(lock) lock sync.Mutex } // NewCountingMultiMap constructs a new counting multimap. func NewCountingMultiMap[T comparable, Q comparable]() *CountingMultiMap[T, Q] { return &CountingMultiMap[T, Q]{ valuesByKey: map[T]*Set[Q]{}, lock: sync.Mutex{}, } } // Add adds the given value to the map at the given key. Returns true if the value // already existed in the map for the given key. func (cmm *CountingMultiMap[T, Q]) Add(key T, value Q) bool { cmm.lock.Lock() defer cmm.lock.Unlock() values, ok := cmm.valuesByKey[key] if !ok { values = NewSet[Q]() cmm.valuesByKey[key] = values } return !values.Add(value) } // Remove removes the given value for the given key from the map. If, after this removal, // the key has no additional values, it is removed entirely from the map. func (cmm *CountingMultiMap[T, Q]) Remove(key T, value Q) { cmm.lock.Lock() defer cmm.lock.Unlock() values, ok := cmm.valuesByKey[key] if !ok { return } values.Delete(value) if values.IsEmpty() { delete(cmm.valuesByKey, key) } }