1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
// Package xmaps contains utilities for working with maps.
package xmaps
import (
"fmt"
"github.com/bradenaw/juniper/xslices"
"github.com/bradenaw/juniper/xsort"
)
// Reverse returns a map from m's values to each of the keys that mapped to it in arbitrary order.
func Reverse[M ~map[K]V, K comparable, V comparable](m M) map[V][]K {
result := make(map[V][]K, len(m))
for k, v := range m {
result[v] = append(result[v], k)
}
return result
}
// ReverseSingle returns a map of m's values to m's keys. If there are any duplicate values, the
// resulting map has an arbitrary choice of the associated keys and the second return is false.
func ReverseSingle[M ~map[K]V, K comparable, V comparable](m M) (map[V]K, bool) {
result := make(map[V]K, len(m))
allOk := true
for k, v := range m {
if _, ok := result[v]; ok {
allOk = false
}
result[v] = k
}
return result, allOk
}
// ToIndex returns a map from keys[i] to i.
func ToIndex[K comparable](keys []K) map[K]int {
m := make(map[K]int, len(keys))
for i := range keys {
m[keys[i]] = i
}
return m
}
// FromKeysAndValues returns a map from keys[i] to values[i]. If there are any duplicate keys, the
// resulting map has an arbitrary choice of the associated values and the second return is false. It
// panics if len(keys)!=len(values).
func FromKeysAndValues[K comparable, V any](keys []K, values []V) (map[K]V, bool) {
if len(keys) != len(values) {
panic(fmt.Sprintf("len(keys)=%d, len(values)=%d", len(keys), len(values)))
}
m := make(map[K]V, len(keys))
allOk := true
for i := range keys {
if _, ok := m[keys[i]]; ok {
allOk = false
}
m[keys[i]] = values[i]
}
return m, allOk
}
// Set[T] is shorthand for map[T]struct{} with convenience methods.
type Set[T comparable] map[T]struct{}
// Add adds item to the set.
func (s Set[T]) Add(item T) { s[item] = struct{}{} }
// Remove removes item from the set.
func (s Set[T]) Remove(item T) { delete(s, item) }
// Contains returns true if item is in the set.
func (s Set[T]) Contains(item T) bool { _, ok := s[item]; return ok }
// SetFromSlice returns a Set whose elements are items.
func SetFromSlice[T comparable](items []T) Set[T] {
result := make(Set[T], len(items))
for _, k := range items {
result[k] = struct{}{}
}
return result
}
// Union returns a set containing all elements of all input sets.
func Union[S ~map[T]struct{}, T comparable](sets ...S) S {
// Size estimate: the smallest possible result is the largest input set, if it's a superset of
// all of the others.
size := 0
for _, set := range sets {
if len(set) > size {
size = len(set)
}
}
out := make(S, size)
for _, set := range sets {
for k := range set {
out[k] = struct{}{}
}
}
return out
}
// Intersection returns a set of the items that all input sets have in common.
func Intersection[S ~map[T]struct{}, T comparable](sets ...S) S {
// The smallest intersection is 0, so don't guess about capacity.
out := make(S)
if len(sets) == 0 {
return out
}
sets = xslices.Clone(sets)
xsort.Slice(sets, func(a, b S) bool { return len(a) < len(b) })
for k := range sets[0] {
include := true
for j := 1; j < len(sets); j++ {
if _, ok := sets[j][k]; !ok {
include = false
break
}
}
if include {
out[k] = struct{}{}
}
}
return out
}
// Intersects returns true if the input sets have any element in common.
func Intersects[S ~map[T]struct{}, T comparable](sets ...S) bool {
if len(sets) == 0 {
return false
}
// Ideally we check from most-selective to least-selective so we can do the fewest iterations
// of each of the below loops. Use set size as an approximation.
sets = xslices.Clone(sets)
xsort.Slice(sets, func(a, b S) bool { return len(a) < len(b) })
for k := range sets[0] {
include := true
for j := 1; j < len(sets); j++ {
if _, ok := sets[j][k]; !ok {
include = false
break
}
}
if include {
return true
}
}
return false
}
// Difference returns all items of a that do not appear in b.
func Difference[S ~map[T]struct{}, T comparable](a, b S) S {
// Size estimate: the smallest possible result is if all items of b are in a.
size := len(a) - len(b)
if size < 0 {
size = 0
}
result := make(S, size)
for k := range a {
if _, ok := b[k]; !ok {
result[k] = struct{}{}
}
}
return result
}
|