{ }codelper
enumscountingP0357

Filter logs by level

A log has five levels, and each level has a name and a code. In declaration order they are DEBUG = 10, INFO = 20, WARNING = 30, ERROR = 40, CRITICAL = 50. Solve this by declaring the five levels as an enum.
Grading looks only at the output. A log line is a level mark at the front, one space, and then the message. The level mark is either a word made only of English letters or an integer from 0 to 100. A word is a level only when it is exactly one of the five names, including upper and lower case. An integer is a level only when it equals one of the five codes. A line with any other level mark is a bad line. A threshold code m is given.
m may be a number that is not one of the five codes. Among the lines that are not bad, print only those whose level code is m or more, in input order. When printing, write the level name even if the mark was a code, then one space, then the message unchanged. Next, for the five levels in declaration order, print one line each with the name and the number of lines of that level, separated by one space.
This number includes lines that were not printed because they were below m. On the last line print bad and the number of bad lines, separated by one space.

Input
The first line has the threshold code m (0 ≤ m ≤ 100) and the number of log lines n (1 ≤ n ≤ 1,000), separated by one space.
Each of the next n lines is one log line.
A word mark has 1 to 15 English letters of either case; an integer mark has no leading zero and no sign.
The message has length 1 to 40, uses only English letters, digits and spaces, and does not start or end with a space
Output
One line "name message" for each line whose level code is m or more, then five lines "name count", then one line "bad count"
Example 1 · stdin
25 7
INFO server started
10 cache warm
WARNING disk 91 percent
warning disk 92 percent
25 odd level
50 out of memory
toString tricky
stdout
WARNING disk 91 percent
CRITICAL out of memory
DEBUG 1
INFO 1
WARNING 1
ERROR 0
CRITICAL 1
bad 3
Example 2 · stdin
30 4
30 exactly at the bar
ERROR db down
20 skipped
3 not a level
stdout
WARNING exactly at the bar
ERROR db down
DEBUG 0
INFO 1
WARNING 1
ERROR 1
CRITICAL 0
bad 1
main.py
Input (stdin)

When you run, these lines are fed to input()/stdin in order (the program doesn't stop to ask).

$ Ready

Open this on a computer to write and run code. On mobile you can read the problem and the solutions.

In other languagesShow solutions by language · 6 languages

This problem accepts only Python, TypeScript, Java, C#, C and C++, the languages that have enums.
JavaScript and Craykon have no enum syntax.
Giving enum members explicit codes, and moving between name, code and enum value, works differently in each language.
In Python Level["INFO"] looks up by name, Level(20) by code, and the name is lv.name.
With IntEnum you can compare with an integer directly, lv >= m; a plain Enum raises TypeError on >=.
Trap one: an unknown name raises KeyError but an unknown code raises ValueError.
Catching only KeyError stops at 25 in the first example.
A Java enum has no code of its own, so you add a field and constructor, DEBUG(10), and write the lookup by code yourself with a loop over values().
ordinal() is not the code but the declaration position 0, 1, 2…, so the solution uses it only as an index into the count array.
Trap two: Level.valueOf(name) does not return null for an unknown name; it throws IllegalArgumentException.
Uncaught, it stops at warning in the first example.
A TypeScript numeric enum is an object with a two-way table: Level.INFO is 20 and Level[20] is "INFO".
Trap three: so Object.keys(Level) also contains "10", "20"….
Looping over it as is prints five extra lines such as 10 0 in the first example.
The solution keeps only the keys that are not numbers as the name list.
Trap four: token in Level is also true for the inherited toString and constructor.
The toString line of the first example is then not counted as bad, giving bad 2.
C# reads both names and codes with one Enum.TryParse(token, out Level level), and printing the value gives the name.
Trap five: TryParse also accepts a code that is not declared and makes (Level)25.
Check again with Enum.IsDefined(typeof(Level), level).
Without it the solution stops with KeyNotFoundException while counting the 25 of the first example.
Trap six: the true in Enum.TryParse(token, true, out level) means ignore case, so the warning line of the first example gets printed too.
A C enum is an int with a label, so the name strings live in a separate NAMES table and are compared with strcmp.
Trap seven: a member without a value is the previous value + 1.
With enum Level { DEBUG = 10, INFO, WARNING, ERROR, CRITICAL } INFO becomes 11 and WARNING 12, so the WARNING line of the first example is not printed and its 50 becomes a bad line.
A C++ enum class gives neither name strings nor a way to loop over all values, so the solution builds a table, and converts with static_cast<int>(lv) to compare with an integer.
Using >= without the cast does not compile.
Trap eight: the threshold is "or more".
With > the 30 line of the second example is dropped.

Pythonmain.py · 44 lines
import sysfrom enum import IntEnum  class Level(IntEnum):    DEBUG = 10    INFO = 20    WARNING = 30    ERROR = 40    CRITICAL = 50  def parse(token):    """이름이나 번호를 등급으로 바꾼다. 등급이 아니면 None."""    try:        if token.isdigit():            return Level(int(token))  # 값으로 찾기: 없는 값이면 ValueError        return Level[token]  # 이름으로 찾기: 없는 이름이면 KeyError    except (ValueError, KeyError):        return None  def main():    lines = sys.stdin.read().split("\n")    m, n = map(int, lines[0].split())    count = {lv: 0 for lv in Level}  # 선언한 차례대로 돈다    bad = 0    out = []    for line in lines[1:1 + n]:        token, text = line.split(" ", 1)        lv = parse(token)        if lv is None:            bad += 1            continue        count[lv] += 1        if lv >= m:  # IntEnum이라 정수와 바로 견줄 수 있다            out.append(f"{lv.name} {text}")    for lv in Level:        out.append(f"{lv.name} {count[lv]}")    out.append(f"bad {bad}")    print("\n".join(out))  main()
TypeScriptmain.ts · 44 lines
import { readFileSync } from "node:fs"; enum Level {  DEBUG = 10,  INFO = 20,  WARNING = 30,  ERROR = 40,  CRITICAL = 50,} // 숫자 enum은 이름 → 값, 값 → 이름 두 방향이 한 객체에 들어 있다.// Object.keys(Level)에는 "10", "20"… 도 나오므로 수가 아닌 열쇠만 골라 이름 목록을 만든다const NAMES: string[] = Object.keys(Level).filter((k) => Number.isNaN(Number(k))); function parse(token: string): Level | undefined {  if (/^[0-9]+$/.test(token)) {    const code = Number(token);    return Level[code] !== undefined ? code : undefined;  }  // Level["toString"]은 물려받은 함수라서 undefined가 아니다. 선언한 이름인지 직접 확인한다  return NAMES.includes(token) ? Level[token as keyof typeof Level] : undefined;} const lines: string[] = readFileSync(0, "utf8").split("\n");const [m, n] = lines[0].split(" ").map(Number);const count = new Map<Level, number>();let bad = 0;const out: string[] = [];for (let i = 1; i <= n; i++) {  const line = lines[i];  const cut = line.indexOf(" ");  const lv = parse(line.slice(0, cut));  if (lv === undefined) {    bad++;    continue;  }  count.set(lv, (count.get(lv) ?? 0) + 1);  if (lv >= m) out.push(`${Level[lv]} ${line.slice(cut + 1)}`);}for (const name of NAMES) {  out.push(`${name} ${count.get(Level[name as keyof typeof Level]) ?? 0}`);}out.push(`bad ${bad}`);console.log(out.join("\n"));
JavaMain.java · 62 lines
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader; public class Main {    // Java 열거형은 값을 스스로 갖지 않는다. 번호는 필드로 달아 준다    enum Level {        DEBUG(10), INFO(20), WARNING(30), ERROR(40), CRITICAL(50);         final int code;         Level(int code) {            this.code = code;        }         static Level fromCode(int code) {            for (Level lv : values()) {                if (lv.code == code) return lv;            }            return null;        }    }     static Level parse(String token) {        if (Character.isDigit(token.charAt(0))) {            return Level.fromCode(Integer.parseInt(token));        }        try {            return Level.valueOf(token); // 없는 이름이면 IllegalArgumentException        } catch (IllegalArgumentException e) {            return null;        }    }     public static void main(String[] args) throws IOException {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        String[] first = br.readLine().split(" ");        int m = Integer.parseInt(first[0]);        int n = Integer.parseInt(first[1]);        int[] count = new int[Level.values().length]; // ordinal()은 0, 1, 2…인 선언 차례다        int bad = 0;        StringBuilder out = new StringBuilder();        for (int i = 0; i < n; i++) {            String line = br.readLine();            int cut = line.indexOf(' ');            Level lv = parse(line.substring(0, cut));            if (lv == null) {                bad++;                continue;            }            count[lv.ordinal()]++;            if (lv.code >= m) {                out.append(lv).append(' ').append(line.substring(cut + 1)).append('\n');            }        }        for (Level lv : Level.values()) {            out.append(lv).append(' ').append(count[lv.ordinal()]).append('\n');        }        out.append("bad ").append(bad).append('\n');        System.out.print(out);    }}
C#Main.cs · 44 lines
using System;using System.Collections.Generic;using System.Text; enum Level{    DEBUG = 10,    INFO = 20,    WARNING = 30,    ERROR = 40,    CRITICAL = 50,} class Program{    static void Main()    {        string[] first = Console.ReadLine().Split(' ');        int m = int.Parse(first[0]);        int n = int.Parse(first[1]);        var count = new Dictionary<Level, int>();        foreach (Level lv in Enum.GetValues(typeof(Level))) count[lv] = 0;        int bad = 0;        var sb = new StringBuilder();        for (int i = 0; i < n; i++)        {            string line = Console.ReadLine();            int cut = line.IndexOf(' ');            string token = line.Substring(0, cut);            // TryParse는 이름과 번호를 모두 받지만 "25"처럼 선언에 없는 번호도 통과시킨다.            // IsDefined로 선언한 값인지 한 번 더 확인한다            if (!Enum.TryParse(token, out Level level) || !Enum.IsDefined(typeof(Level), level))            {                bad++;                continue;            }            count[level]++;            if ((int)level >= m) sb.Append(level).Append(' ').Append(line.Substring(cut + 1)).Append('\n');        }        foreach (Level lv in Enum.GetValues(typeof(Level))) sb.Append(lv).Append(' ').Append(count[lv]).Append('\n');        sb.Append("bad ").Append(bad).Append('\n');        Console.Write(sb);    }}
C++main.cpp · 55 lines
#include <array>#include <cctype>#include <iostream>#include <string>using namespace std; enum class Level { DEBUG = 10, INFO = 20, WARNING = 30, ERROR = 40, CRITICAL = 50 }; // enum class는 이름 문자열도, 모든 값을 도는 방법도 주지 않아서 표를 만든다const array<pair<Level, string>, 5> TABLE = {{    {Level::DEBUG, "DEBUG"},    {Level::INFO, "INFO"},    {Level::WARNING, "WARNING"},    {Level::ERROR, "ERROR"},    {Level::CRITICAL, "CRITICAL"},}}; // 등급이면 표의 몇 번째인지, 아니면 -1int parse(const string& token) {    bool number = isdigit(static_cast<unsigned char>(token[0])) != 0;    for (int k = 0; k < static_cast<int>(TABLE.size()); k++) {        if (number) {            // enum class는 정수와 바로 견줄 수 없어서 static_cast로 바꾼다            if (stoi(token) == static_cast<int>(TABLE[k].first)) return k;        } else if (token == TABLE[k].second) {            return k;        }    }    return -1;} int main() {    ios::sync_with_stdio(false);    cin.tie(nullptr);    int m, n;    cin >> m >> n;    string line;    getline(cin, line);    array<int, 5> count{};    int bad = 0;    for (int i = 0; i < n; i++) {        getline(cin, line);        size_t cut = line.find(' ');        int k = parse(line.substr(0, cut));        if (k < 0) {            bad++;            continue;        }        count[k]++;        if (static_cast<int>(TABLE[k].first) >= m) cout << TABLE[k].second << ' ' << line.substr(cut + 1) << '\n';    }    for (int k = 0; k < 5; k++) cout << TABLE[k].second << ' ' << count[k] << '\n';    cout << "bad " << bad << '\n';    return 0;}
Cmain.c · 48 lines
#include <ctype.h>#include <stdio.h>#include <stdlib.h>#include <string.h> /* 값을 적지 않으면 앞의 값 + 1이 된다. 다섯 개 모두 적는다 */enum Level { DEBUG = 10, INFO = 20, WARNING = 30, ERROR = 40, CRITICAL = 50 }; #define KINDS 5/* C 열거형은 이름표가 붙은 정수일 뿐이라 이름 문자열은 따로 표로 만든다 */static const enum Level LEVELS[KINDS] = { DEBUG, INFO, WARNING, ERROR, CRITICAL };static const char *NAMES[KINDS] = { "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL" }; /* 등급이면 표의 몇 번째인지, 아니면 -1 */static int parse(const char *token) {    for (int k = 0; k < KINDS; k++) {        if (isdigit((unsigned char)token[0])) {            if (atoi(token) == (int)LEVELS[k]) return k;        } else if (strcmp(token, NAMES[k]) == 0) {            return k;        }    }    return -1;} int main(void) {    char line[128];    int m, n;    if (!fgets(line, sizeof line, stdin) || sscanf(line, "%d %d", &m, &n) != 2) return 0;    int count[KINDS] = { 0 };    int bad = 0;    for (int i = 0; i < n; i++) {        if (!fgets(line, sizeof line, stdin)) return 0;        line[strcspn(line, "\n")] = '\0';        char *cut = strchr(line, ' ');        *cut = '\0';        int k = parse(line);        if (k < 0) {            bad++;            continue;        }        count[k]++;        if ((int)LEVELS[k] >= m) printf("%s %s\n", NAMES[k], cut + 1);    }    for (int k = 0; k < KINDS; k++) printf("%s %d\n", NAMES[k], count[k]);    printf("bad %d\n", bad);    return 0;}