func EquivalentSlices(a, b []string) bool {
	if a == nil && b == nil {
		return true
	}
	if a == nil || b == nil {
		return false
	}
	mapA := map[string]bool{}
	mapB := map[string]bool{}
	for _, keyA := range a {
		mapA[keyA] = true
	}
	for _, keyB := range b {
		mapB[keyB] = true
	}
	var sortedA, sortedB []string
	for keyA := range mapA {
		sortedA = append(sortedA, keyA)
	}
	for keyB := range mapB {
		sortedB = append(sortedB, keyB)
	}
	sort.Strings(sortedA)
	sort.Strings(sortedB)
	if len(sortedA) != len(sortedB) {
		return false
	}
	for i := range sortedA {
		if sortedA[i] != sortedB[i] {
			return false
		}
	}
	return true
}
EquivalentSlices checks whether the given string sets are equivalent, as in,
they contain the same values.
package strutil
import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"sort"
	"strings"
	"github.com/hashicorp/errwrap"
	glob "github.com/ryanuber/go-glob"
)
func StrListContainsGlob(haystack []string, needle string) bool {
	for _, item := range haystack {
		if glob.Glob(item, needle) {
			return true
		}
	}
	return false
}
func StrListContains(haystack []string, needle string) bool {
	for _, item := range haystack {
		if item == needle {
			return true
		}
	}
	return false
}
func StrListSubset(super, sub []string) bool {
	for _, item := range sub {
		if !StrListContains(super, item) {
			return false
		}
	}
	return true
}
func ParseDedupAndSortStrings(input string, sep string) []string {
	input = strings.TrimSpace(input)
	parsed := []string{}
	if input == "" {
		return parsed
	}
	return RemoveDuplicates(strings.Split(input, sep), false)
}
func ParseDedupLowercaseAndSortStrings(input string, sep string) []string {
	input = strings.TrimSpace(input)
	parsed := []string{}
	if input == "" {
		return parsed
	}
	return RemoveDuplicates(strings.Split(input, sep), true)
}
func ParseKeyValues(input string, out map[string]string, sep string) error {
	if out == nil {
		return fmt.Errorf("'out is nil")
	}
	keyValues := ParseDedupLowercaseAndSortStrings(input, sep)
	if len(keyValues) == 0 {
		return nil
	}
	for _, keyValue := range keyValues {
		shards := strings.Split(keyValue, "=")
		if len(shards) != 2 {
			return fmt.Errorf("invalid <key,value> format")
		}
		key := strings.TrimSpace(shards[0])
		value := strings.TrimSpace(shards[1])
		if key == "" || value == "" {
			return fmt.Errorf("invalid <key,value> pair: key: %q value: %q", key, value)
		}
		out[key] = value
	}
	return nil
}
func ParseArbitraryKeyValues(input string, out map[string]string, sep string) error {
	input = strings.TrimSpace(input)
	if input == "" {
		return nil
	}
	if out == nil {
		return fmt.Errorf("'out' is nil")
	}
	inputBytes, err := base64.StdEncoding.DecodeString(input)
	if err == nil {
		input = string(inputBytes)
	}
	err = json.Unmarshal([]byte(input), &out)
	if err != nil {
		if err = ParseKeyValues(input, out, sep); err != nil {
			return errwrap.Wrapf("failed to parse the input: {{err}}", err)
		}
	}
	for key, value := range out {
		if key != "" && value == "" {
			return fmt.Errorf("invalid value for key %q", key)
		}
	}
	return nil
}
func ParseStringSlice(input string, sep string) []string {
	input = strings.TrimSpace(input)
	if input == "" {
		return []string{}
	}
	splitStr := strings.Split(input, sep)
	ret := make([]string, len(splitStr))
	for i, val := range splitStr {
		ret[i] = strings.TrimSpace(val)
	}
	return ret
}
func ParseArbitraryStringSlice(input string, sep string) []string {
	input = strings.TrimSpace(input)
	if input == "" {
		return []string{}
	}
	inputBytes, err := base64.StdEncoding.DecodeString(input)
	if err == nil {
		input = string(inputBytes)
	}
	ret := []string{}
	err = json.Unmarshal([]byte(input), &ret)
	if err != nil {
		return ParseStringSlice(input, sep)
	}
	if ret == nil {
		return []string{}
	}
	return ret
}
func TrimStrings(items []string) []string {
	ret := make([]string, len(items))
	for i, item := range items {
		ret[i] = strings.TrimSpace(item)
	}
	return ret
}
func RemoveDuplicates(items []string, lowercase bool) []string {
	itemsMap := map[string]bool{}
	for _, item := range items {
		item = strings.TrimSpace(item)
		if lowercase {
			item = strings.ToLower(item)
		}
		if item == "" {
			continue
		}
		itemsMap[item] = true
	}
	items = make([]string, 0, len(itemsMap))
	for item := range itemsMap {
		items = append(items, item)
	}
	sort.Strings(items)
	return items
}
func RemoveDuplicatesStable(items []string, caseInsensitive bool) []string {
	itemsMap := make(map[string]bool, len(items))
	deduplicated := make([]string, 0, len(items))
	for _, item := range items {
		key := strings.TrimSpace(item)
		if caseInsensitive {
			key = strings.ToLower(key)
		}
		if key == "" || itemsMap[key] {
			continue
		}
		itemsMap[key] = true
		deduplicated = append(deduplicated, item)
	}
	return deduplicated
}
func RemoveEmpty(items []string) []string {
	if len(items) == 0 {
		return items
	}
	itemsSlice := make([]string, 0, len(items))
	for _, item := range items {
		if item == "" {
			continue
		}
		itemsSlice = append(itemsSlice, item)
	}
	return itemsSlice
}