学ぶ › TypeScript
さらに先へ · 25
レコードのリストと列挙型
レコードをリストに集め、決まったいくつかの値には名前を付けます。
いくつかの値をまとめたレコードをリストに順に入れると、一枚の表になります。
そして「低い・中くらい・高い」のように決まったいくつかのうちの一つである値には、名前を付けておきます(列挙型、enum)。
下の例は、生徒の名前と点数を読んで等級を付けたレコードをリストに集め、1行ずつ出力したあと、等級が HIGH の生徒の名前だけを出力します。
from enum import Enum class Level(Enum): # 정해진 세 가지 중 하나 LOW = 0 MID = 1 HIGH = 2 def level_of(score): if score >= 80: return Level.HIGH if score >= 60: return Level.MID return Level.LOW n = int(input())students = [] # 묶음 자료(이름, 점수, 등급)의 목록for _ in range(n): name, score = input().split() score = int(score) students.append({"name": name, "score": score, "level": level_of(score)}) for s in students: print(s["name"], s["score"], s["level"].name) high = []for s in students: if s["level"] == Level.HIGH: high.append(s["name"])print("HIGH:", *high)// enum이 없어서, 바꿀 수 없게 얼린 객체에 이름을 모아 둬요const Level = Object.freeze({ LOW: "LOW", MID: "MID", HIGH: "HIGH" }); function levelOf(score) { if (score >= 80) return Level.HIGH; if (score >= 60) return Level.MID; return Level.LOW;} const lines = require("fs").readFileSync(0, "utf8").trim().split("\n");const n = Number(lines[0]);const students = []; // 묶음 자료(이름, 점수, 등급)의 목록for (let i = 1; i <= n; i++) { const [name, scoreText] = lines[i].trim().split(" "); const score = Number(scoreText); students.push({ name: name, score: score, level: levelOf(score) });} for (const s of students) { console.log(s.name + " " + s.score + " " + s.level);} const high = [];for (const s of students) { if (s.level === Level.HIGH) high.push(s.name);}console.log("HIGH: " + high.join(" "));import { readFileSync } from "node:fs"; enum Level { LOW, MID, HIGH } // 정해진 세 가지 중 하나(속은 0, 1, 2) type Student = { name: string; score: number; level: Level }; function levelOf(score: number): Level { if (score >= 80) return Level.HIGH; if (score >= 60) return Level.MID; return Level.LOW;} const lines: string[] = readFileSync(0, "utf8").trim().split("\n");const n: number = Number(lines[0]);const students: Student[] = []; // 묶음 자료(이름, 점수, 등급)의 목록for (let i = 1; i <= n; i++) { const [name, scoreText] = lines[i].trim().split(" "); const score = Number(scoreText); students.push({ name: name, score: score, level: levelOf(score) });} for (const s of students) { console.log(s.name + " " + s.score + " " + Level[s.level]); // Level[2]는 이름 "HIGH"} const high: string[] = [];for (const s of students) { if (s.level === Level.HIGH) high.push(s.name);}console.log("HIGH: " + high.join(" "));import java.util.*; public class Main { enum Level { LOW, MID, HIGH } // 정해진 세 가지 중 하나 record Student(String name, int score, Level level) {} static Level levelOf(int score) { if (score >= 80) return Level.HIGH; if (score >= 60) return Level.MID; return Level.LOW; } public static void main(String[] args) { Scanner in = new Scanner(System.in); int n = in.nextInt(); List<Student> students = new ArrayList<>(); // 묶음 자료(이름, 점수, 등급)의 목록 for (int i = 0; i < n; i++) { String name = in.next(); int score = in.nextInt(); students.add(new Student(name, score, levelOf(score))); } for (Student s : students) { System.out.println(s.name() + " " + s.score() + " " + s.level()); } List<String> high = new ArrayList<>(); for (Student s : students) { if (s.level() == Level.HIGH) high.add(s.name()); } System.out.println("HIGH: " + String.join(" ", high)); }}using System;using System.Collections.Generic; enum Level { LOW, MID, HIGH } // 정해진 세 가지 중 하나 record Student(string Name, int Score, Level Level); class Program{ static Level LevelOf(int score) { if (score >= 80) return Level.HIGH; if (score >= 60) return Level.MID; return Level.LOW; } static void Main() { int n = int.Parse(Console.ReadLine()); var students = new List<Student>(); // 묶음 자료(이름, 점수, 등급)의 목록 for (int i = 0; i < n; i++) { string[] parts = Console.ReadLine().Split(); int score = int.Parse(parts[1]); students.Add(new Student(parts[0], score, LevelOf(score))); } foreach (Student s in students) { Console.WriteLine(s.Name + " " + s.Score + " " + s.Level); } var high = new List<string>(); foreach (Student s in students) { if (s.Level == Level.HIGH) high.Add(s.Name); } Console.WriteLine("HIGH: " + string.Join(" ", high)); }}#include <iostream>#include <string>#include <vector> enum class Level { LOW, MID, HIGH }; // 정해진 세 가지 중 하나const char* LEVEL_NAMES[] = {"LOW", "MID", "HIGH"}; // 이름은 따로 적어 둬요 struct Student { std::string name; int score; Level level;}; Level levelOf(int score) { if (score >= 80) return Level::HIGH; if (score >= 60) return Level::MID; return Level::LOW;} int main() { int n; std::cin >> n; std::vector<Student> students; // 묶음 자료(이름, 점수, 등급)의 목록 for (int i = 0; i < n; i++) { std::string name; int score; std::cin >> name >> score; students.push_back({name, score, levelOf(score)}); } for (const Student& s : students) { std::cout << s.name << ' ' << s.score << ' ' << LEVEL_NAMES[static_cast<int>(s.level)] << '\n'; } std::cout << "HIGH:"; for (const Student& s : students) { if (s.level == Level::HIGH) std::cout << ' ' << s.name; } std::cout << '\n';}#include <stdio.h> typedef enum { LOW, MID, HIGH } Level; // 정해진 세 가지 중 하나(속은 0, 1, 2)const char *LEVEL_NAMES[] = {"LOW", "MID", "HIGH"}; // 이름은 따로 적어 둬요 typedef struct { char name[32]; int score; Level level;} Student; Level level_of(int score) { if (score >= 80) return HIGH; if (score >= 60) return MID; return LOW;} int main(void) { int n; scanf("%d", &n); Student students[100]; // 묶음 자료(이름, 점수, 등급)의 배열 for (int i = 0; i < n; i++) { scanf("%31s %d", students[i].name, &students[i].score); students[i].level = level_of(students[i].score); } for (int i = 0; i < n; i++) { printf("%s %d %s\n", students[i].name, students[i].score, LEVEL_NAMES[students[i].level]); } printf("HIGH:"); for (int i = 0; i < n; i++) { if (students[i].level == HIGH) printf(" %s", students[i].name); } printf("\n"); return 0;}// enum이 없어서, 이름 붙인 상수로 정해요(바꾸지 않기로 약속한 변수)LOW = 0;MID = 1;HIGH = 2;LEVEL_NAMES = ["LOW", "MID", "HIGH"]; levelOf(score){ もし (score >= 80) { 返す HIGH; } もし (score >= 60) { 返す MID; } 返す LOW;} n = コンソール.行読み().数に();students = []; // 묶음 자료(이름, 점수, 등급)의 목록繰り返す (i = 0; i < n; i++){ parts = コンソール.行読み().分ける(); s = 辞書(); // 바퀴마다 새 묶음 자료를 만들어요 s["name"] = parts[0]; s["score"] = parts[1].数に(); s["level"] = levelOf(s["score"]); students.追加(s);} それぞれ (s の中 students){ コンソール.書く(s["name"] + " " + s["score"] + " " + LEVEL_NAMES[s["level"]]);} high = [];それぞれ (s の中 students){ もし (s["level"] == HIGH) { high.追加(s["name"]); }}コンソール.書く("HIGH: " + high.つなぐ(" "));4 mina 92 jun 58 sora 75 theo 81
mina 92 HIGH jun 58 LOW sora 75 MID theo 81 HIGH HIGH: mina theo
ポイント
- レコード一つは、一人(一つの物)についての値のまとまりです。
これをリストに入れると、一つの場所がレコード一つである表になります。studentsから取り出したsの名前・点数・等級を一緒に扱います。 - レコードはループの中で、毎回新しく作ってリストに入れます。
名前のリストと点数のリストを別々に持つより、一人分の値がばらばらにならなくて済みます。 - 列挙型(enum)は、決まったいくつかの値に名前を付けた型です。
文字列"HIGH"や数2を直接書く代わりにLevel.HIGHと書きます。
列挙型のある言語では、Level.HIHGのように存在しない名前を書くとエラーで知らせてくれます(Python は実行中に、ほかはコンパイル時に)。 - 列挙型の中身は言語ごとに違います。
C、C++、TypeScript、C# は中身が 0、1、2 のような数です。
名前を出力するとき、Java と C# はそのまま出力すればよく、Python は.name、TypeScript はLevel[値]、C と C++ は名前を書いておいた配列から取り出します。 - 列挙型のない JavaScript と Craykon は、名前を付けた定数で同じことをします。
コードのあちこちに2と書く代わりに、HIGHという名前を一度決めて使います。 - 点数を等級に変える仕事は、関数一つ(
levelOf)にまとめます。
基準が変わっても、直すのは一か所だけです。
言語ごとの書き方
| Python | レコードは辞書 {"name": name, "score": score} で作り、students.append(...) で入れて s["name"] で取り出します。@dataclass を付けたクラスで作れば s.name で取り出せます。列挙型は from enum import Enum のあと、class Level(Enum): の中に HIGH = 2 のように書きます。名前は level.name、中の値は level.value、名前の文字列から探すときは Level["HIGH"] |
|---|---|
| JavaScript | レコードはオブジェクト { name: name, score: score } で作り、students.push(...) で入れて s.name で取り出します。変数名とキーが同じなら { name, score } と短く書けます。enum がないので、 const Level = Object.freeze({ LOW: "LOW", MID: "MID", HIGH: "HIGH" }); のように凍結したオブジェクトを使います。凍結したオブジェクトは Level.HIGH = "x" としても変わりません。このサイトの採点のように普通のスクリプトとして動かすとエラーなしで無視され、厳格モード( "use strict" や ES モジュール)では TypeError で止まります。存在しない名前 Level.HIHG はエラーではなく undefined です |
| TypeScript | レコードの形は type Student = { name: string; score: number; level: Level }; で決め、リストは Student[]。列挙型は enum Level { LOW, MID, HIGH } で、中身は 0、1、2 です。名前は Level[s.level] で得ます。いくつかの文字列のうちの一つと決める type Level = "LOW" | "MID" | "HIGH"; もよく使います |
| Java | レコードは record Student(String name, int score, Level level) {} で作り、new Student(...)、取り出すときは s.name()。リストは List<Student>。列挙型は enum Level { LOW, MID, HIGH } で、出力すると名前がそのまま出ます。順番の番号は level.ordinal()、名前の文字列から探すときは Level.valueOf("HIGH") |
| C# | レコードは record Student(string Name, int Score, Level Level); で作り、new Student(...)、取り出すときは s.Name。リストは List<Student>。列挙型は enum Level { LOW, MID, HIGH } で、出力すると名前が出ます。中の数は (int)level、名前の文字列から探すときは Enum.Parse<Level>("HIGH") |
| C++ | レコードは struct Student { std::string name; int score; Level level; };、リストは std::vector<Student> に push_back({name, score, levelOf(score)})。列挙型は enum class Level { LOW, MID, HIGH }; で、Level::HIGH と書きます。enum class は std::cout に直接入れられず、int にひとりでに変わることもないので、名前の配列から LEVEL_NAMES[static_cast<int>(s.level)] で取り出します |
| C | レコードは typedef struct { char name[32]; int score; Level level; } Student;、リストは配列 Student students[100]; で students[i].score のように使います。列挙型は typedef enum { LOW, MID, HIGH } Level; で、中身は整数 0、1、2 です。名前を出力する機能がないので、 LEVEL_NAMES[students[i].level] のように名前の配列から取り出します |
| Craykon | 構造体がないので、レコードは辞書で作ります。 毎回 s = 辞書(); で新しく作り、s["name"] = ... で埋めてから students.追加(s)。enum もないので、いちばん上に LOW = 0; MID = 1; HIGH = 2; のように名前を付けた定数を置き、名前はリスト LEVEL_NAMES[s["level"]] から取り出します。定数を変えられないようにする機能はないので、変えないと約束します |
よくある間違い
列挙型を出力したら名前ではなく数が出たTypeScript · C · C++
enum Level { LOW, MID, HIGH }
console.log(Level.HIGH);→ 2 — 中に入っている数がそのまま出ます。C の printf("%d\n", level); も、C++ で enum Level を std::cout に入れても 2 です。C++ の enum class を std::cout に入れるとコンパイルエラーです
TypeScript は Level[Level.HIGH] で名前を得ます。
C と C++ は {"LOW", "MID", "HIGH"} のような名前の配列を作って、そこから取り出します。
一つのレコードを書き換えながら何度も入れたJavaScript · 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' } ] — リストの二つの場所が同じ一つのレコードを指しているので、最後に書いた値だけが残ります
レコードをループの中で作ります(const s = { name: name };)。
そうすれば毎回新しいレコードができます。
列挙型の値を文字列や数と比べたPython
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 は文字列 "HIGH" でも数 2 でもない、別の値です。エラーなしで偽になるので見つけにくいです
level == Level.HIGH のように列挙型どうしで比べます。
入力で受け取った文字列は、先に Level["HIGH"] で列挙型に変えます。
列挙型だからといって、決まった値しか入らないわけではありません。
C では
Level x = 7; がそのままコンパイルでき、7 が出力されます。C# で
(Level)7 を出力しても 7 です。C++ の
enum class は Level x = 7; をコンパイルエラーで止めてくれます。外から来た数を列挙型に変えるときは、先に範囲を確かめます。