学ぶ › TypeScript
さらに先へ · 24
数える・累計・グループ化
一度たどりながら、条件に合うものを数え、ここまでの値を持ち歩き、キーごとにまとめます。
数える、累計、グループ化は、データを一度たどりながら答えを作っていく三つの基本の形です。
下の例は点数を一つずつ見ながら、ここまでの合計と最高点を表示し、80 点以上がいくつあるかを数え、点数を 60 点台・70 点台のように十の位でまとめて表示します。
scores = list(map(int, input().split()))count = 0 # 80점 이상인 점수의 개수total = 0 # 지금까지의 합best = scores[0] # 지금까지의 최고 점수groups = {} # 십의 자리(60, 70, …) → 그 묶음의 점수들for s in scores: if s >= 80: count += 1 total += s if s > best: best = s print(total, best) key = s // 10 * 10 if key not in groups: groups[key] = [] # 처음 보는 열쇠면 빈 목록부터 groups[key].append(s)print(count)for key in sorted(groups): print(f"{key}:", *groups[key])const scores = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number);let count = 0; // 80점 이상인 점수의 개수let total = 0; // 지금까지의 합let best = scores[0]; // 지금까지의 최고 점수const groups = new Map(); // 십의 자리(60, 70, …) → 그 묶음의 점수들for (const s of scores) { if (s >= 80) count++; total += s; if (s > best) best = s; console.log(total + " " + best); const key = Math.floor(s / 10) * 10; if (!groups.has(key)) groups.set(key, []); // 처음 보는 열쇠면 빈 목록부터 groups.get(key).push(s);}console.log(count);for (const key of [...groups.keys()].sort((a, b) => a - b)) { console.log(key + ": " + groups.get(key).join(" "));}import { readFileSync } from "node:fs";const scores: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number);let count = 0; // 80점 이상인 점수의 개수let total = 0; // 지금까지의 합let best = scores[0]; // 지금까지의 최고 점수const groups = new Map<number, number[]>(); // 십의 자리(60, 70, …) → 그 묶음의 점수들for (const s of scores) { if (s >= 80) count++; total += s; if (s > best) best = s; console.log(total + " " + best); const key = Math.floor(s / 10) * 10; let group = groups.get(key); if (group === undefined) { // 처음 보는 열쇠면 빈 목록부터 group = []; groups.set(key, group); } group.push(s);}console.log(count);for (const key of [...groups.keys()].sort((a, b) => a - b)) { console.log(key + ": " + groups.get(key)!.join(" "));}import java.util.*; public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); List<Integer> scores = new ArrayList<>(); while (in.hasNextInt()) scores.add(in.nextInt()); int count = 0; // 80점 이상인 점수의 개수 int total = 0; // 지금까지의 합 int best = scores.get(0); // 지금까지의 최고 점수 Map<Integer, List<Integer>> groups = new TreeMap<>(); // 십의 자리 → 그 묶음의 점수들(열쇠 순) for (int s : scores) { if (s >= 80) count++; total += s; if (s > best) best = s; System.out.println(total + " " + best); int key = s / 10 * 10; if (!groups.containsKey(key)) groups.put(key, new ArrayList<>()); // 처음 보는 열쇠면 빈 목록부터 groups.get(key).add(s); } System.out.println(count); for (int key : groups.keySet()) { StringBuilder line = new StringBuilder(key + ":"); for (int s : groups.get(key)) line.append(" ").append(s); System.out.println(line); } }}using System;using System.Collections.Generic;using System.Linq; class Program{ static void Main() { int[] scores = Console.ReadLine().Split().Select(int.Parse).ToArray(); int count = 0; // 80점 이상인 점수의 개수 int total = 0; // 지금까지의 합 int best = scores[0]; // 지금까지의 최고 점수 var groups = new SortedDictionary<int, List<int>>(); // 십의 자리 → 그 묶음의 점수들(열쇠 순) foreach (int s in scores) { if (s >= 80) count++; total += s; if (s > best) best = s; Console.WriteLine(total + " " + best); int key = s / 10 * 10; if (!groups.ContainsKey(key)) groups[key] = new List<int>(); // 처음 보는 열쇠면 빈 목록부터 groups[key].Add(s); } Console.WriteLine(count); foreach (int key in groups.Keys) { Console.WriteLine(key + ": " + string.Join(" ", groups[key])); } }}#include <iostream>#include <map>#include <vector> int main() { std::vector<int> scores; int x; while (std::cin >> x) scores.push_back(x); int count = 0; // 80점 이상인 점수의 개수 int total = 0; // 지금까지의 합 int best = scores[0]; // 지금까지의 최고 점수 std::map<int, std::vector<int>> groups; // 십의 자리 → 그 묶음의 점수들(열쇠 순) for (int s : scores) { if (s >= 80) count++; total += s; if (s > best) best = s; std::cout << total << ' ' << best << '\n'; int key = s / 10 * 10; groups[key].push_back(s); // 없는 열쇠면 []가 빈 vector를 만들어 줘요 } std::cout << count << '\n'; for (const auto& [key, group] : groups) { std::cout << key << ':'; for (int s : group) std::cout << ' ' << s; std::cout << '\n'; }}#include <stdio.h> int main(void) { int scores[100], n = 0; while (n < 100 && scanf("%d", &scores[n]) == 1) n++; int count = 0; // 80점 이상인 점수의 개수 int total = 0; // 지금까지의 합 int best = scores[0]; // 지금까지의 최고 점수 // 사전이 없어서 묶음을 배열로: groups[6]이 60점대(점수는 0~100), sizes[6]이 그 개수 int groups[11][100], sizes[11] = {0}; for (int i = 0; i < n; i++) { int s = scores[i]; if (s >= 80) count++; total += s; if (s > best) best = s; printf("%d %d\n", total, best); int key = s / 10; groups[key][sizes[key]++] = s; } printf("%d\n", count); for (int key = 0; key <= 10; key++) { if (sizes[key] == 0) continue; // 비어 있는 묶음은 건너뛰어요 printf("%d:", key * 10); for (int j = 0; j < sizes[key]; j++) printf(" %d", groups[key][j]); printf("\n"); } return 0;}scores = コンソール.数読み();count = 0; // 80점 이상인 점수의 개수total = 0; // 지금까지의 합best = scores[0]; // 지금까지의 최고 점수groups = 辞書(); // 십의 자리(60, 70, …) → 그 묶음의 점수들それぞれ (s の中 scores){ もし (s >= 80) { count++; } total += s; もし (s > best) { best = s; } コンソール.書く(total + " " + best); key = 数学.商(s, 10) * 10; もし (!groups.含む(key)) { groups[key] = []; // 처음 보는 열쇠면 빈 목록부터 } groups[key].追加(s);}コンソール.書く(count);keys = groups.キー一覧;keys.並べ替え();それぞれ (key の中 keys){ コンソール.書く(key + ": " + groups[key].つなぐ(" "));}72 85 90 68 85 77 93
72 72 157 85 247 90 315 90 400 90 477 90 570 93 4 60: 68 70: 72 77 80: 85 85 90: 90 93
ポイント
- 数えるときは 0 から始めて、条件に合うときだけ 1 を足します。
合計と違って、値ではなく 1 を足します。 - 累計の値は、一つ見るたびに書き換える変数です。
ここまでの合計(total)、ここまでの最高(best)のように「ここまでの答え」をいつも持っているので、途中の結果をその場で表示できます。
繰り返しの単元で見たtotal += iがまさにこの形です。 - 累計の変数は繰り返しの前で作ります。
個数と合計は 0 から、最高・最低は最初の値から始めます。 - グループ化は、値ごとにキーを決めて(例では 68 → 60、72 → 70)、辞書の中のそのキーのリストに値を入れます。
数える辞書(辞書の単元)は値が数でしたが、まとめる辞書は値がリストです。 - 初めて見るキーには、まだリストがありません。
先に空のリストを入れてから、値を追加します。 - 三つを別々にたどる必要はありません。
例は一つの繰り返しの中で、数える・累計・グループ化をまとめて行います。
言語ごとの書き方
| Python | 数えるのは count += 1(1行なら sum(1 for s in scores if s >= 80))、累計は total += s。グループ化は例のように if key not in groups: groups[key] = [] のあとで groups[key].append(s)、短く書くなら groups.setdefault(key, []).append(s) や collections.defaultdict(list) |
|---|---|
| JavaScript | 数えるのは count++(1行なら scores.filter((s) => s >= 80).length)、累計は total += s。グループ化は Map に if (!groups.has(key)) groups.set(key, []); のあとで groups.get(key).push(s) |
| TypeScript | JavaScript と同じで、new Map<number, number[]>() のように型を書きます。groups.get(key) は型の上では undefined かもしれないので、そのまま .push すると型エラー(TS2532)です。例のように取り出した値を変数に受けて、 undefined かどうか確かめます |
| Java | 数えるのは count++(1行なら scores.stream().filter(s -> s >= 80).count())、累計は total += s。グループ化は Map<Integer, List<Integer>> に containsKey で確かめて put(key, new ArrayList<>())、短く書くなら groups.computeIfAbsent(key, k -> new ArrayList<>()).add(s)。TreeMap ならキー順にたどれます |
| C# | 数えるのは count++(LINQ なら scores.Count(s => s >= 80))、累計は total += s。グループ化は Dictionary<int, List<int>> に if (!groups.ContainsKey(key)) groups[key] = new List<int>(); のあとで groups[key].Add(s)。SortedDictionary ならキー順にたどれます |
| C++ | 数えるのは count++(<algorithm> の std::count_if もあります)、累計は total += s。グループ化は std::map<int, std::vector<int>> に groups[key].push_back(s) の1行です。ないキーなら [] が空の vector を作ってくれます |
| C | 数えるのと累計はほかの言語と同じです(count++、total += s)。辞書がないので、グループ化は手で行います。 キーが小さい整数なら、例のように2次元配列に groups[key][sizes[key]++] = s で入れ、グループごとの個数(sizes)を別に数えます |
| Craykon | 数えるのは count++、累計は total += s。グループ化は 辞書() に もし (!groups.含む(key)) { groups[key] = []; } のあとで groups[key].追加(s)。キー順に表示するには groups.キー一覧 を 並べ替え() してからたどります |
よくある間違い
+= を =+ と書いた
int count = 0;
int scores[] = {72, 85, 90};
for (int i = 0; i < 3; i++)
if (scores[i] >= 80) count =+ 1;
printf("%d\n", count);→ 1 — エラーなしで動きますが、2 になりません。count =+ 1 は count = +1、つまり「1 を入れる」です
count += 1 と書きます。
個数がいつも 1 になるときは、まずこの2文字の順番を確かめます。
累計の変数を繰り返しの中で 0 にした
const scores = [72, 85, 90];
for (const s of scores) {
let total = 0;
total += s;
console.log(total);
}→ 72、85、90 — 1周ごとに 0 に戻るので、合計がたまりません
total の最初の値(0)は繰り返しの前で一度だけ入れ、繰り返しの中では足すだけにします。let や int のような宣言の言葉がない Python と Craykon でも、total = 0 が繰り返しの中にあれば同じく 72、85、90 になります。
空のリストを作らずにグループへ入れたJava
Map<Integer, List<Integer>> groups = new TreeMap<>();
int s = 68;
int key = s / 10 * 10;
groups.get(key).add(s);→ NullPointerException — ないキーなので get が null を返しました
先に if (!groups.containsKey(key)) groups.put(key, new ArrayList<>()); とするか、groups.computeIfAbsent(key, k -> new ArrayList<>()).add(s) と書きます。
言語ごとの違い
グループごとにリストを別々に作る必要があります。
Python で
例のように、初めて見るキーごとに新しい
グループごとにリストを別々に作る必要があります。
Python で
groups = dict.fromkeys([60, 70], []) とキーを先に用意すると、すべてのキーが同じ一つのリストを共有するので、68 と 72 を別のキーに入れても {60: [68, 72], 70: [68, 72]} になります。例のように、初めて見るキーごとに新しい
[] を作りましょう。