1package interactors
2
3import (
4	"regexp"
5	"sort"
6	"strings"
7)
8
9type simpleSet map[string]bool
10
11var unicode = regexp.MustCompile("[^\x00-\x7F]+")
12
13func (s simpleSet) Contains(other string) bool {
14	enabled, exists := s[other]
15	return exists && enabled
16}
17
18// Returns the different of two slices of strings; effectively a set arithmetic operation on two slices of strings
19func DistinctValues(slice1, slice2 []string) []string {
20	sets := []simpleSet{
21		sliceToSimpleSet(slice1),
22		sliceToSimpleSet(slice2),
23	}
24
25	discardKeysFromOther(sets[0], sets[1])
26	discardKeysFromOther(sets[1], sets[0])
27
28	var exclusiveValues []string
29	for _, k := range allKeys(sets...) {
30		for _, set := range sets {
31			if set[k] == true {
32				exclusiveValues = append(exclusiveValues, k)
33			}
34		}
35	}
36	sort.Strings(exclusiveValues)
37	return exclusiveValues
38}
39
40func SetSubtract(add, negate []string) []string {
41	toRemove := sliceToSimpleSet(negate)
42	var result []string
43	for _, a := range add {
44		if !toRemove.Contains(a) {
45			result = append(result, a)
46		}
47	}
48	return result
49}
50
51func SetUnion(slice1, slice2 []string) []string {
52	union := allKeys(
53		sliceToSimpleSet(
54			append(
55				slice1,
56				slice2...,
57			),
58		),
59	)
60	sort.Strings(union)
61	return union
62}
63
64func sliceToSimpleSet(s []string) simpleSet {
65	m := make(simpleSet, len(s))
66	for _, val := range s {
67		m[val] = true
68	}
69	return m
70}
71
72func discardKeysFromOther(s1, s2 simpleSet) {
73	for k := range s1 {
74		if _, exists := s2[k]; exists {
75			s2[k] = false
76		}
77	}
78}
79
80func allKeys(sets ...simpleSet) []string {
81	totalCount := 0
82	for _, s := range sets {
83		totalCount += len(s)
84	}
85
86	keys := make([]string, totalCount)
87	index := 0
88	for _, s := range sets {
89		for k := range s {
90			keys[index] = k
91			index++
92		}
93	}
94	return keys
95}
96
97func FilterNoUnicode(s string) string {
98	badCharacters := sliceToSimpleSet(
99		unicode.FindAllString(s, -1),
100	)
101	if len(badCharacters) == 0 {
102		return s
103	}
104	validCharacters := make([]string, 0, len(s))
105	for _, rune_ := range s {
106		char := string(rune_)
107		if !badCharacters.Contains(char) {
108			validCharacters = append(validCharacters, char)
109		}
110	}
111	return strings.Join(
112		validCharacters,
113		"",
114	)
115}
116