学习 › JavaScript
进阶 · 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(一行写法: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++(一行写法: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++(一行写法: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)。键不存在时, [] 会自动创建一个空的 vector |
| C | 计数和累计与其他语言相同(count++、total += s)。没有字典,所以分组要自己动手。 键是较小的整数时,像示例那样用二维数组 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,先检查这两个字符的顺序。
在循环里把累计变量设成 0
const scores = [72, 85, 90];
for (const s of scores) {
let total = 0;
total += s;
console.log(total);
}→ 72、85、90 — 每一轮都回到 0,总和累积不起来
total 的初始值(0)只在循环之前给一次,循环里只做加法。
Python 和 Craykon 没有 let、int 这样的声明词,但只要 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]}。要像示例那样,每次遇到新的键都新建一个
[]。