File: xmaps.go

package info (click to toggle)
golang-github-bradenaw-juniper 0.15.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 872 kB
  • sloc: sh: 27; makefile: 2
file content (169 lines) | stat: -rw-r--r-- 4,332 bytes parent folder | download
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
}