github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/utils/slices.go (about)

     1  /*
     2  Copyright 2021 Gravitational, Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package utils
    18  
    19  import (
    20  	"slices"
    21  	"strings"
    22  )
    23  
    24  // JoinStrings returns a string that is all the elements in the slice `T[]` joined by `sep`
    25  // This being generic allows for the usage of custom string times, without having to convert
    26  // the elements to a string to be passed into `strings.Join`.
    27  func JoinStrings[T ~string](elems []T, sep string) T {
    28  	switch len(elems) {
    29  	case 0:
    30  		return ""
    31  	case 1:
    32  		return elems[0]
    33  	}
    34  	n := len(sep) * (len(elems) - 1)
    35  	for i := 0; i < len(elems); i++ {
    36  		n += len(elems[i])
    37  	}
    38  
    39  	var b strings.Builder
    40  	b.Grow(n)
    41  	b.WriteString(string(elems[0]))
    42  	for _, s := range elems[1:] {
    43  		b.WriteString(sep)
    44  		b.WriteString(string(s))
    45  	}
    46  	return T(b.String())
    47  }
    48  
    49  // Deduplicate deduplicates list of comparable values.
    50  func Deduplicate[T comparable](in []T) []T {
    51  	if len(in) <= 1 {
    52  		return in
    53  	}
    54  	out := make([]T, 0, len(in))
    55  	seen := make(map[T]struct{}, len(in))
    56  	for _, val := range in {
    57  		if _, ok := seen[val]; !ok {
    58  			out = append(out, val)
    59  			seen[val] = struct{}{}
    60  		}
    61  	}
    62  	return out
    63  }
    64  
    65  // DeduplicateAny deduplicates list of any values with compare function.
    66  func DeduplicateAny[T any](in []T, compare func(T, T) bool) []T {
    67  	if len(in) <= 1 {
    68  		return in
    69  	}
    70  	out := make([]T, 0, len(in))
    71  	for _, val := range in {
    72  		var seen bool
    73  		for _, outVal := range out {
    74  			if compare(val, outVal) {
    75  				seen = true
    76  				break
    77  			}
    78  		}
    79  		if !seen {
    80  			out = append(out, val)
    81  		}
    82  	}
    83  	return out
    84  }
    85  
    86  // ContainSameUniqueElements returns true if the input slices contain the same
    87  // unique elements. Ordering and duplicates are ignored.
    88  func ContainSameUniqueElements[S ~[]E, E comparable](s1, s2 S) bool {
    89  	s1Dedup := Deduplicate(s1)
    90  	s2Dedup := Deduplicate(s2)
    91  
    92  	if len(s1Dedup) != len(s2Dedup) {
    93  		return false
    94  	}
    95  	for i := range s1Dedup {
    96  		if !slices.Contains(s2Dedup, s1Dedup[i]) {
    97  			return false
    98  		}
    99  	}
   100  	return true
   101  }
   102  
   103  // Any checks if any element of slice satisfy given predicate. If the slice is empty, it returns false.
   104  func Any[S ~[]E, E any](s S, predicate func(E) bool) bool {
   105  	for _, e := range s {
   106  		if predicate(e) {
   107  			return true
   108  		}
   109  	}
   110  	return false
   111  }
   112  
   113  // All checks if all elements of slice satisfy given predicate. If the slice is empty, it returns true.
   114  func All[S ~[]E, E any](s S, predicate func(E) bool) bool {
   115  	for _, e := range s {
   116  		if !predicate(e) {
   117  			return false
   118  		}
   119  	}
   120  	return true
   121  }
   122  
   123  // CountBy counts the occurrences of each element in a slice based on a given mapper function.
   124  func CountBy[S ~[]E, E any](elements S, mapper func(E) string) map[string]int {
   125  	out := make(map[string]int)
   126  	for _, elem := range elements {
   127  		key := mapper(elem)
   128  		out[key] += 1
   129  	}
   130  	return out
   131  }