
At Acme Mobile, different app layers emit inconsistent error strings for the same failure. Implement a reusable error normalization function that groups similar raw error messages under a canonical error key.
Write a function group_errors(errors, rules) that takes a list of raw error strings and a list of normalization rules. Each rule is a pair [pattern, canonical]. For every error string, first normalize it by converting to lowercase and replacing every maximal sequence of digits with #. Then, if the normalized string contains one or more rule patterns, assign the error to the canonical value of the longest matching pattern. If no pattern matches, use the normalized string itself as the key. Return a dictionary mapping each final key to its frequency.
errors: list of stringsrules: list of [pattern, canonical] string pairsExample 1
errors = ["Timeout after 30 seconds", "timeout after 45 seconds"], rules = [["timeout after # seconds", "TIMEOUT"]]{"TIMEOUT": 2}Example 2
errors = ["HTTP 500 on /feed", "HTTP 404 on /feed"], rules = [["http #", "HTTP_ERROR"]]{"HTTP_ERROR": 2}#, so both messages match the same rule.1 <= len(errors) <= 10^40 <= len(rules) <= 10^31 <= len(error) <= 2001 <= len(pattern), len(canonical) <= 200errors = ["Timeout after 30 seconds", "timeout after 45 seconds"], rules = [["timeout after # seconds", "TIMEOUT"]]Output{"TIMEOUT": 2}WhyBoth strings normalize to `timeout after # seconds`, so they are grouped under the same canonical key.errors = ["HTTP 500 on /feed", "HTTP 404 on /feed"], rules = [["http #", "HTTP_ERROR"]]Output{"HTTP_ERROR": 2}WhyAfter normalization, both messages contain `http #`, so both count toward `HTTP_ERROR`.errors = ["User 123 not found", "User 456 not found", "Disk full"], rules = [["user # not found", "USER_NOT_FOUND"]]Output{"USER_NOT_FOUND": 2, "disk full": 1}WhyThe first two messages match the rule after normalization; `Disk full` has no matching rule and keeps its normalized text.1 <= len(errors) <= 10^40 <= len(rules) <= 10^31 <= len(error) <= 2001 <= len(pattern), len(canonical) <= 200If multiple rules match, choose the canonical value from the longest matching patterndef group_errors(errors, rules):