{ }codelper

Learn › Craykon

Going further · 25

Lists of records and enums

Collect records in a list, and give names to a small fixed set of values.

Put records (bundles of several values) into a list one after another and you have a table.
And a value that is one of a few fixed choices, like low, middle or high, gets a name (an enum).
The example below reads students' names and scores, collects records with a level in a list, prints them line by line, and then prints only the names of the students whose level is HIGH.

// enum이 없어서, 이름 붙인 상수로 정해요(바꾸지 않기로 약속한 변수)LOW = 0;MID = 1;HIGH = 2;LEVEL_NAMES = ["LOW", "MID", "HIGH"]; levelOf(score){    if (score >= 80)    {        return HIGH;    }    if (score >= 60)    {        return MID;    }    return LOW;} n = Console.ReadLine().ToNumber();students = []; // 묶음 자료(이름, 점수, 등급)의 목록for (i = 0; i < n; i++){    parts = Console.ReadLine().Split();    s = Dict(); // 바퀴마다 새 묶음 자료를 만들어요    s["name"] = parts[0];    s["score"] = parts[1].ToNumber();    s["level"] = levelOf(s["score"]);    students.Add(s);} foreach (s in students){    Console.WriteLine(s["name"] + " " + s["score"] + " " + LEVEL_NAMES[s["level"]]);} high = [];foreach (s in students){    if (s["level"] == HIGH)    {        high.Add(s["name"]);    }}Console.WriteLine("HIGH: " + high.Join(" "));
stdin
4
mina 92
jun 58
sora 75
theo 81
stdout
mina 92 HIGH
jun 58 LOW
sora 75 MID
theo 81 HIGH
HIGH: mina theo

Key points

  • One record is the bundle of values about one person (or thing).
    Put records in a list and you get a table where each slot is one record.
    Take one s out of students and its name, score and level come together.
  • Create a new record on every pass inside the loop and add it to the list.
    Compared with keeping a list of names and a separate list of scores, one person's values do not get scattered.
  • An enum is a type that names a small fixed set of values.
    Instead of writing the string "HIGH" or the number 2 directly, you write Level.HIGH.
    In the languages that have enums, a name that does not exist, such as Level.HIHG, is reported as an error (while running in Python, at compile time in the others).
  • What is inside an enum differs by language.
    In C, C++, TypeScript and C# it is a number such as 0, 1, 2.
    To print the name, Java and C# just print it, Python uses .name, TypeScript uses Level[value], and C and C++ look it up in an array of names.
  • JavaScript and Craykon have no enum, so they do the same job with named constants.
    Instead of writing 2 all over the code, you define the name HIGH once and use it.
  • Turning a score into a level is kept in one function (levelOf).
    When the rule changes, there is only one place to fix.

How each language writes it

PythonMake a record as a dictionary {"name": name, "score": score}, add it with students.append(...) and read it with s["name"].
With a class marked @dataclass you read it as s.name.
For an enum, write from enum import Enum, then lines like HIGH = 2 inside class Level(Enum):.
The name is level.name, the inner value is level.value, and Level["HIGH"] looks one up by its name string
JavaScriptMake a record as an object { name: name, score: score }, add it with students.push(...) and read it with s.name.
When the variable and the key have the same name, shorten it to { name, score }.
There is no enum, so use a frozen object: const Level = Object.freeze({ LOW: "LOW", MID: "MID", HIGH: "HIGH" });.
A frozen object does not change even after Level.HIGH = "x".
Run as a plain script, as this site's judge does, the assignment is ignored with no error; in strict mode ("use strict" or an ES module) it stops with a TypeError.
A missing name such as Level.HIHG is not an error but undefined
TypeScriptDescribe the record's shape with type Student = { name: string; score: number; level: Level }; and the list is Student[].
The enum is enum Level { LOW, MID, HIGH }, with 0, 1, 2 inside.
Get the name with Level[s.level].
A type that is one of a few strings, type Level = "LOW" | "MID" | "HIGH";, is also widely used
JavaMake a record with record Student(String name, int score, Level level) {}, create it with new Student(...) and read it with s.name().
The list is List<Student>.
The enum is enum Level { LOW, MID, HIGH }, and printing it gives the name as is.
Its position is level.ordinal(), and Level.valueOf("HIGH") looks one up by its name string
C#Make a record with record Student(string Name, int Score, Level Level);, create it with new Student(...) and read it with s.Name.
The list is List<Student>.
The enum is enum Level { LOW, MID, HIGH }, and printing it gives the name.
The inner number is (int)level, and Enum.Parse<Level>("HIGH") looks one up by its name string
C++The record is struct Student { std::string name; int score; Level level; }; and the list is a std::vector<Student> with push_back({name, score, levelOf(score)}).
The enum is enum class Level { LOW, MID, HIGH };, used as Level::HIGH.
An enum class cannot go straight into std::cout and does not turn into an int by itself, so take the name from an array: LEVEL_NAMES[static_cast<int>(s.level)]
CThe record is typedef struct { char name[32]; int score; Level level; } Student; and the list is an array Student students[100];, used like students[i].score.
The enum is typedef enum { LOW, MID, HIGH } Level;, with the integers 0, 1, 2 inside.
Nothing prints the name for you, so take it from an array of names: LEVEL_NAMES[students[i].level]
CraykonThere are no structs, so a record is a dictionary.
On each pass make a new one with s = Dict();, fill it with s["name"] = ... and add it with students.Add(s).
There is no enum either, so put named constants at the top, LOW = 0; MID = 1; HIGH = 2;, and take the name from a list, LEVEL_NAMES[s["level"]].
Nothing stops a constant from being changed, so it is a promise not to change it

Common mistakes

Adding one record again and again while editing itJavaScript · TypeScript · Python · Craykon

const s = {};
const students = [];
for (const name of ["mina", "jun"]) {
  s.name = name;
  students.push(s);
}
console.log(students);

→ [ { name: 'jun' }, { name: 'jun' } ] — both slots of the list point at the same single record, so only the value written last remains

Create the record inside the loop (const s = { name: name };).
Then every pass makes a new record.

Comparing an enum value with a string or a numberPython

from enum import Enum
class Level(Enum):
    LOW = 0
    MID = 1
    HIGH = 2
level = Level.HIGH
print(level == "HIGH", level == 2)

→ False False — Level.HIGH is neither the string "HIGH" nor the number 2; it is a value of its own. It becomes false with no error, which makes it hard to find

Compare enum with enum: level == Level.HIGH.
Turn a string from the input into the enum first with Level["HIGH"].

Printing an enum gave a number, not the nameTypeScript · C · C++

enum Level { LOW, MID, HIGH }
console.log(Level.HIGH);

→ 2 — the number inside comes out as is. printf("%d\n", level); in C and sending an enum Level to std::cout in C++ also give 2. Sending a C++ enum class to std::cout is a compile error

In TypeScript get the name with Level[Level.HIGH].
In C and C++ make an array of names such as {"LOW", "MID", "HIGH"} and take it from there.

How languages differ
Being an enum does not mean only the listed values can get in.
In C, Level x = 7; compiles as is and prints 7.
Printing (Level)7 in C# gives 7 too.
A C++ enum class stops Level x = 7; with a compile error.
When turning a number from outside into an enum, check its range first.

Practice