pkg/common/collection.go (89 lines of code) (raw):

// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package common import ( "slices" "sort" "strings" ) func DedupStringArray(arr []string) []string { arrByMap := map[string]struct{}{} for _, v := range arr { arrByMap[v] = struct{}{} } deduped := []string{} for v := range arrByMap { deduped = append(deduped, v) } slices.SortFunc(deduped, strings.Compare) return deduped } func levenshteinDistance(s, t string) int { m := len(s) n := len(t) d := make([][]int, m+1) for i := range d { d[i] = make([]int, n+1) } for i := 1; i <= m; i++ { d[i][0] = i } for j := 1; j <= n; j++ { d[0][j] = j } for i := 1; i <= m; i++ { for j := 1; j <= n; j++ { m := min(d[i-1][j]+1, d[i][j-1]+1) if s[i-1] == t[j-1] { d[i][j] = min(m, d[i-1][j-1]) } else { d[i][j] = min(m, d[i-1][j-1]+1) } } } return d[m][n] } func SortForAutocomplete(input string, elements []string) []string { // Sort criteria // Priority group 1: completely matches the name // Priority group 2: has the query prefix and levenshtein distance // Priority 3: levenshtein distance result := []string{} prefixMatches := []string{} prefixNonMatches := []string{} for _, element := range elements { if element == input { result = append(result, element) continue } if strings.HasPrefix(element, input) { prefixMatches = append(prefixMatches, element) } else { prefixNonMatches = append(prefixNonMatches, element) } } distances := map[string]int{} for _, s := range elements { distances[s] = levenshteinDistance(input, s) } sort.Slice(prefixMatches, func(i, j int) bool { if distances[prefixMatches[i]] == distances[prefixMatches[j]] { return strings.Compare(prefixMatches[i], prefixMatches[j]) > 0 } return distances[prefixMatches[i]] < distances[prefixMatches[j]] }) sort.Slice(prefixNonMatches, func(i, j int) bool { if distances[prefixNonMatches[i]] == distances[prefixNonMatches[j]] { return strings.Compare(prefixNonMatches[i], prefixNonMatches[j]) > 0 } return distances[prefixNonMatches[i]] < distances[prefixNonMatches[j]] }) return append(append(result, prefixMatches...), prefixNonMatches...) } // SameStringSet checks if the set of elements are same or not. func SameStringSet(a, b []string) bool { a = DedupStringArray(a) b = DedupStringArray(b) if len(a) != len(b) { return false } for i := range a { if a[i] != b[i] { return false } } return true }