k8s.io/test-infra/triage@v0.0.0-20240520184403-27c6b4c223d8/summarize/map_abstractions.go (about) 1 /* 2 Copyright 2020 The Kubernetes Authors. 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 /* 18 This file contains abstractions of certain complex map types that are used often throughout 19 summarize.go. They also have some often-used methods such as getting just the keys, or getting the 20 key-value pairs as a map. 21 */ 22 23 package summarize 24 25 import ( 26 "sort" 27 ) 28 29 // map[string][]failure type alias 30 31 // failuresGroup maps strings to failure slices. 32 type failuresGroup map[string][]failure 33 34 // failuresGroupPair is a representation of a failuresGroup key-value mapping as a two-element 35 // struct. 36 type failuresGroupPair struct { 37 Key string `json:"key"` 38 Failures []failure `json:"failures"` 39 } 40 41 // keys provides the failuresGroup's keys as a string slice. 42 func (fg *failuresGroup) keys() []string { 43 result := make([]string, 0, len(*fg)) 44 45 for key := range *fg { 46 result = append(result, key) 47 } 48 49 return result 50 } 51 52 // asSlice returns the failuresGroup as a failuresGroupPair slice. 53 func (fg *failuresGroup) asSlice() []failuresGroupPair { 54 result := make([]failuresGroupPair, 0, len(*fg)) 55 56 for str, failures := range *fg { 57 result = append(result, failuresGroupPair{str, failures}) 58 } 59 60 return result 61 } 62 63 // sortByMostFailures returns a failuresGroupPair slice sorted by the number of failures in each 64 // pair, descending. If the number of failures is the same for two pairs, they are sorted alphabetically 65 // by their keys. 66 func (fg *failuresGroup) sortByMostFailures() []failuresGroupPair { 67 result := fg.asSlice() 68 69 // Sort the slice. 70 sort.Slice(result, func(i, j int) bool { 71 iFailures := len(result[i].Failures) 72 jFailures := len(result[j].Failures) 73 74 if iFailures == jFailures { 75 return result[i].Key < result[j].Key 76 } 77 78 return iFailures > jFailures 79 }) 80 81 return result 82 } 83 84 // equal determines whether this failuresGroup is deeply equal to another failuresGroup. 85 func (a *failuresGroup) equal(b *failuresGroup) bool { 86 // First check the length to deal with different-length maps 87 if len(*a) != len(*b) { 88 return false 89 } 90 91 for key, failuresA := range *a { 92 // Make sure the other map contains the same keys 93 if failuresB, ok := (*b)[key]; ok { 94 // Check lengths 95 if len(failuresA) != len(failuresB) { 96 return false 97 } 98 // Compare the failures slices 99 for i := range failuresA { 100 if failuresA[i] != failuresB[i] { 101 return false 102 } 103 } 104 } else { 105 // The other map is missing a key 106 return false 107 } 108 } 109 110 return true 111 } 112 113 // map[string]failuresGroup type alias, which is really a map[string]map[string][]failure type alias 114 115 // nestedFailuresGroups maps strings to failuresGroup instances. 116 type nestedFailuresGroups map[string]failuresGroup 117 118 // nestedFailuresGroupsPair is a representation of a nestedFailuresGroups key-value mapping as a 119 // two-element struct. 120 type nestedFailuresGroupsPair struct { 121 Key string `json:"key"` 122 Group failuresGroup `json:"group"` 123 } 124 125 // keys provides the nestedFailuresGroups's keys as a string slice. 126 func (nfg *nestedFailuresGroups) keys() []string { 127 result := make([]string, len(*nfg)) 128 129 iter := 0 130 for key := range *nfg { 131 result[iter] = key 132 iter++ 133 } 134 135 return result 136 } 137 138 // asSlice returns the nestedFailuresGroups as a nestedFailuresGroupsPair slice. 139 func (nfg *nestedFailuresGroups) asSlice() []nestedFailuresGroupsPair { 140 result := make([]nestedFailuresGroupsPair, len(*nfg)) 141 142 iter := 0 143 for str, group := range *nfg { 144 result[iter] = nestedFailuresGroupsPair{str, group} 145 iter++ 146 } 147 148 return result 149 } 150 151 // sortByMostAggregatedFailures returns a nestedFailuresGroupsPair slice sorted by the aggregate 152 // number of failures across all failure slices in each failuresGroup, descending. If the aggregate 153 // number of failures is the same for two pairs, they are sorted alphabetically by their keys. 154 func (nfg *nestedFailuresGroups) sortByMostAggregatedFailures() []nestedFailuresGroupsPair { 155 result := nfg.asSlice() 156 157 // Pre-compute the aggregate failures for each element of result so that the less 158 // function doesn't have to compute it on every compare. 159 // aggregates maps nestedFailuresGroups strings to number of aggregate failures across all of 160 // their failure slices. 161 aggregates := make(map[string]int, len(*nfg)) 162 for str, fg := range *nfg { 163 aggregate := 0 164 for _, group := range fg { 165 aggregate += len(group) 166 } 167 aggregates[str] = aggregate 168 } 169 170 // Sort the slice. 171 sort.Slice(result, func(i, j int) bool { 172 if aggregates[result[i].Key] == aggregates[result[j].Key] { 173 return result[i].Key < result[j].Key 174 } 175 176 return aggregates[result[i].Key] > aggregates[result[j].Key] 177 }) 178 179 return result 180 } 181 182 // equal determines whether this nestedFailuresGroups object is deeply equal to another nestedFailuresGroups object. 183 func (a *nestedFailuresGroups) equal(b *nestedFailuresGroups) bool { 184 // First check the length to deal with different-length maps 185 if len(*a) != len(*b) { 186 return false 187 } 188 189 for key, failuresGroupA := range *a { 190 // Make sure the other map contains the same keys 191 if failuresGroupB, ok := (*b)[key]; ok { 192 if !failuresGroupA.equal(&failuresGroupB) { 193 return false 194 } 195 } else { 196 // The other map is missing a key 197 return false 198 } 199 } 200 201 return true 202 }