grokify/mogo

View on GitHub
type/maputil/maputil.go

Summary

Maintainability
A
0 mins
Test Coverage
package maputil

import (
    "errors"
    "fmt"
    "sort"
    "strings"

    "github.com/grokify/mogo/sort/sortutil"
    "golang.org/x/exp/constraints"
    "golang.org/x/exp/slices"
)

// StringKeys takes a map where the keys are strings and reurns a slice of key names.
// An optional transform function, `xf`, can be supplied along with an option to sort
// the results. If both transform and sort are requested, the sort is performed on the
// transformed strings.
func StringKeys[V any](m map[string]V, xf func(s string) string) []string {
    var keys []string
    for k := range m {
        if xf != nil {
            k = xf(k)
        }
        keys = append(keys, k)
    }
    sort.Strings(keys)
    return keys
}

// IsSubset checks to see if `submap` is a subset of `m`.
func IsSubset[C comparable, K comparable](m, submap map[C]K) bool {
    for k, v := range submap {
        if v2, ok := m[k]; !ok || v != v2 {
            return false
        }
    }
    return true
}

// IsSubsetOrValues checks to see if any of the values of `submap` are present in `a` where
// all keys exist`.
func IsSubsetOrValues[C comparable, K comparable](m map[C]K, submap map[C][]K) bool {
    for k, vals := range submap {
        if v, ok := m[k]; !ok || !slices.Contains(vals, v) {
            return false
        }
    }
    return true
}

// Keys returns a list of sorted keys.
func Keys[K constraints.Ordered, V any](m map[K]V) []K {
    var keys []K
    for k := range m {
        keys = append(keys, k)
    }
    sortutil.Slice(keys)
    return keys
}

// KeysExist checks to verify if a set of keys exists within a map with string keys.
// If `requireAll` is set, then all keys must be present for the function to return `true`.
// If `requireAll` is not set, then only one key must exist for the function to return `true`.
func KeysExist[K comparable, V any](m map[K]V, keys []K, requireAll bool) bool {
    for _, k := range keys {
        _, ok := m[k]
        if requireAll && !ok {
            return false
        } else if !requireAll && ok {
            return true
        }
    }
    if requireAll {
        return true
    } else {
        return false
    }
}

// ValuesSorted returns a string slice of sorted values.
func ValuesSorted[K comparable, V constraints.Ordered](m map[K]V) []V {
    var vals []V
    for _, val := range m {
        vals = append(vals, val)
    }
    sortutil.Slice(vals)
    return vals
}

func Values[K constraints.Ordered, V any](m map[K]V) []V {
    keys := Keys(m)
    var vals []V
    for _, k := range keys {
        v, ok := m[k]
        if !ok {
            panic("key not found")
        }
        vals = append(vals, v)
    }
    return vals
}

func NumberValuesMergeSum[K comparable, V constraints.Float | constraints.Integer](m ...map[K]V) map[K]V {
    merged := map[K]V{}
    for _, m1 := range m {
        for k, v := range m1 {
            merged[k] += v
        }
    }
    return merged
}

// NumberValuesAverage returns a `float64` average of a map's values.
func NumberValuesAverage[K comparable, V constraints.Float | constraints.Integer](m map[K]V) float64 {
    if len(m) == 0 {
        return 0
    }
    sum := float64(0)
    for _, v := range m {
        sum += float64(v)
    }
    return sum / float64(len(m))
}

var ErrMapDuplicateValues = errors.New("map has duplicate values")

// UniqueValues returns `true` if the number of unique values is the same as the number of keys.
func UniqueValues[C comparable](m map[C]C) bool {
    rev := map[C]C{}
    for k, v := range m {
        if _, ok := rev[v]; ok {
            return false
        }
        rev[v] = k
    }
    return len(rev) == len(m)
}

func DuplicateValues[C comparable](m map[C]C) map[C][]C {
    dupes := map[C][]C{}
    for k, v := range m {
        if _, ok := dupes[v]; !ok {
            dupes[v] = []C{}
        }
        dupes[v] = append(dupes[v], k)
    }
    for k, v := range dupes {
        if len(v) == 1 {
            delete(dupes, k)
        }
    }
    return dupes
}

func ValueIntOrDefault[K comparable, V constraints.Integer](m map[K]V, key K, def V) V {
    if val, ok := m[key]; ok {
        return val
    }
    return def
}

func ValueStringOrDefault[K comparable](m map[K]string, key K, def string) string {
    if val, ok := m[key]; ok {
        return val
    }
    return def
}

func MapSSToKeyValues(kvs map[string]string, sep string) string {
    var pairs []string
    for k, v := range kvs {
        k = strings.Trim(k, sep)
        v = strings.Trim(v, sep)
        if len(k) > 0 {
            pairs = append(pairs, k+"="+v)
        }
    }
    return strings.Join(pairs, sep)
}

type MapInt64Int64 map[int64]int64

func (m MapInt64Int64) KeysSorted() []int64 {
    return Keys(m)
}

func (m MapInt64Int64) ValuesSortedByKeys() []int64 {
    var vals []int64
    keys := m.KeysSorted()
    for _, k := range keys {
        if v, ok := m[k]; ok {
            vals = append(vals, v)
        } else {
            panic(fmt.Sprintf("key not found [%d]", k))
        }
    }
    return vals
}