더 나아가기 · 16
사전(맵)
열쇠로 값을 찾고 고치기: 단어 세기, 있나 확인, 열쇠 순서로 돌기.
사전(맵)은 번호 대신 열쇠로 값을 찾아요.
아래 예제는 단어마다 몇 번 나왔는지 세고, 물어본 단어를 찾고, 열쇠 순서로 모두 찍어요.
words = input().split()queries = input().split()print(len(set(words)))counts = {}for w in words: counts[w] = counts.get(w, 0) + 1for q in queries: if q in counts: print(q, counts[q]) else: print(q, "none")for w in sorted(counts): print(w, counts[w])const [words, queries] = require("fs").readFileSync(0, "utf8").trim().split("\n").map((l) => l.trim().split(/\s+/));console.log(new Set(words).size);const counts = new Map();for (const w of words) counts.set(w, (counts.get(w) ?? 0) + 1);for (const q of queries) { if (counts.has(q)) console.log(q, counts.get(q)); else console.log(q, "none");}for (const w of [...counts.keys()].sort()) console.log(w, counts.get(w));import { readFileSync } from "node:fs";const [words, queries]: string[][] = readFileSync(0, "utf8").trim().split("\n").map((l) => l.trim().split(/\s+/));console.log(new Set(words).size);const counts = new Map<string, number>();for (const w of words) counts.set(w, (counts.get(w) ?? 0) + 1);for (const q of queries) { if (counts.has(q)) console.log(q, counts.get(q)); else console.log(q, "none");}for (const w of [...counts.keys()].sort()) console.log(w, counts.get(w));import java.util.*; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); String[] words = sc.nextLine().trim().split("\\s+"); String[] queries = sc.nextLine().trim().split("\\s+"); System.out.println(new HashSet<>(Arrays.asList(words)).size()); Map<String, Integer> counts = new TreeMap<>(); for (String w : words) counts.merge(w, 1, Integer::sum); for (String q : queries) { if (counts.containsKey(q)) System.out.println(q + " " + counts.get(q)); else System.out.println(q + " none"); } for (Map.Entry<String, Integer> e : counts.entrySet()) { System.out.println(e.getKey() + " " + e.getValue()); } }}using System;using System.Collections.Generic;using System.Linq; class Program{ static void Main() { string[] words = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries); string[] queries = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries); Console.WriteLine(new HashSet<string>(words).Count); var counts = new Dictionary<string, int>(); foreach (string w in words) counts[w] = counts.GetValueOrDefault(w) + 1; foreach (string q in queries) { if (counts.TryGetValue(q, out int c)) Console.WriteLine($"{q} {c}"); else Console.WriteLine($"{q} none"); } foreach (string w in counts.Keys.OrderBy(k => k, StringComparer.Ordinal)) Console.WriteLine($"{w} {counts[w]}"); }}#include <iostream>#include <map>#include <set>#include <sstream>#include <string>#include <vector> std::vector<std::string> readWords() { std::string line, w; std::getline(std::cin, line); std::istringstream in(line); std::vector<std::string> out; while (in >> w) out.push_back(w); return out;} int main() { std::vector<std::string> words = readWords(), queries = readWords(); std::cout << std::set<std::string>(words.begin(), words.end()).size() << '\n'; std::map<std::string, int> counts; for (const auto& w : words) counts[w]++; for (const auto& q : queries) { if (counts.contains(q)) std::cout << q << ' ' << counts.at(q) << '\n'; else std::cout << q << " none\n"; } for (const auto& [w, c] : counts) std::cout << w << ' ' << c << '\n';}#include <stdio.h>#include <stdlib.h>#include <string.h> char keys[1000][32];int vals[1000], n = 0; int find(const char *w) { for (int i = 0; i < n; i++) if (strcmp(keys[i], w) == 0) return i; return -1;} int cmp(const void *a, const void *b) { return strcmp(keys[*(const int *)a], keys[*(const int *)b]); } int main(void) { char line[4096], qline[4096]; fgets(line, sizeof line, stdin); fgets(qline, sizeof qline, stdin); for (char *w = strtok(line, " \r\n"); w; w = strtok(NULL, " \r\n")) { int i = find(w); if (i < 0) { strcpy(keys[n], w); vals[n] = 0; i = n++; } vals[i]++; } printf("%d\n", n); for (char *q = strtok(qline, " \r\n"); q; q = strtok(NULL, " \r\n")) { int i = find(q); if (i >= 0) printf("%s %d\n", q, vals[i]); else printf("%s none\n", q); } int order[1000]; for (int i = 0; i < n; i++) order[i] = i; qsort(order, n, sizeof order[0], cmp); for (int i = 0; i < n; i++) printf("%s %d\n", keys[order[i]], vals[order[i]]); return 0;}words = 콘솔.줄읽기().나누기();queries = 콘솔.줄읽기().나누기();counts = 사전();각각 (w 에서 words){ 만약 (counts.있나(w)) { counts[w] = counts[w] + 1; } 아니면 { counts[w] = 1; }}콘솔.쓰기(counts.개수);각각 (q 에서 queries){ 만약 (counts.있나(q)) { 콘솔.쓰기(q + " " + counts[q]); } 아니면 { 콘솔.쓰기(q + " none"); }}keys = counts.열쇠목록;keys.정렬();각각 (w 에서 keys){ 콘솔.쓰기(w + " " + counts[w]);}apple banana apple cherry banana apple banana grape
3 banana 2 grape none apple 3 banana 2 cherry 1
핵심
"apple" → 3처럼 열쇠와 값을 짝지어 둬요.
넣기·찾기·고치기가 목록을 처음부터 훑는 것보다 훨씬 빨라요.- 가장 흔한 쓰임은 세기예요.
처음 보는 단어는 0에서 시작해 1을 더해요. - 없는 열쇠를 읽을 때가 언어마다 가장 많이 달라요.
오류가 나거나, 빈 값이 나오거나, 새 칸이 생겨요.
그래서 먼저 있나 확인해요. - 도는 순서를 믿지 마세요.
넣은 차례를 지키는 언어도 있고, 순서를 약속하지 않는 언어도 있어요.
예제는 어디서나 같도록 열쇠를 정렬해서 찍어요. - 값 없이 열쇠만 모은 것이 집합이에요.
서로 다른 단어 수는 집합의 크기로 바로 구해요.
언어별로 쓰는 법
| Python | counts = {} — 세기 counts.get(w, 0) + 1, 확인 w in counts.없는 열쇠를 []로 읽으면 KeyError.넣은 차례를 지켜요. 집합은 set |
|---|---|
| JavaScript | new Map() — 세기 (counts.get(w) ?? 0) + 1, 확인 has.없으면 get이 undefined.넣은 차례를 지켜요. 집합은 Set |
| TypeScript | JavaScript와 같아요.new Map<string, number>()처럼 타입을 적어요 |
| Java | HashMap(순서 약속 없음), LinkedHashMap(넣은 차례), TreeMap(열쇠 순).세기 merge(w, 1, Integer::sum), 확인 containsKey, 없으면 get이 null.집합은 HashSet |
| C# | Dictionary<string, int> — 세기 GetValueOrDefault(w) + 1, 확인 TryGetValue.없는 열쇠를 인덱서로 읽으면 KeyNotFoundException.순서는 정해져 있지 않아요(열쇠 순은 SortedDictionary).집합은 HashSet |
| C++ | std::map(열쇠 순), std::unordered_map(순서 약속 없음).세기 counts[w]++, 확인 C++20 contains.없는 열쇠를 []로 읽으면 값 0인 칸이 새로 생겨요.집합은 std::set |
| C | 사전이 없어요. 예제는 열쇠 배열과 값 배열을 나란히 두고 처음부터 찾아요(작은 입력에는 충분해요) |
| Craykon | 사전() — d["사과"] = 3으로 넣고 읽어요.확인은 d.있나(열쇠), d.열쇠목록은 넣은 차례.없는 열쇠를 읽으면 Python처럼 실행 오류로 멈춰요 |
자주 하는 실수
확인하려고 []로 읽었더니 칸이 생겼어요C++
std::map<std::string, int> counts;
if (counts["grape"] == 0) { }
std::cout << counts.size();→ 1 — 읽기만 했는데 "grape" 칸이 값 0으로 생겼어요
확인은 counts.contains(w)(C++20)나 counts.find(w)로 해요.
넣은 차례대로 나올 줄 알았어요Java · C# · C++
Map<String, Integer> m = new HashMap<>();
m.put("pear", 1); m.put("apple", 2); m.put("fig", 3);
System.out.println(m.keySet());→ [apple, pear, fig] — 넣은 차례(pear, apple, fig)와 달라요
열쇠를 정렬해서 찍거나, 순서를 약속하는 TreeMap·LinkedHashMap(C++ std::map, C# SortedDictionary)을 써요.
없는 열쇠를 바로 읽었어요Python · C# · Craykon
counts = {"apple": 3}
print(counts["grape"])→ KeyError: 'grape'로 멈춰요(C#은 KeyNotFoundException, 크레이콘은 사전에 "grape" 열쇠가 없어요)
먼저 있나 확인하거나, Python get(w, 0)처럼 기본값을 줘요.
언어마다 다른 점
JavaScript에서 빈 객체
세기에는
JavaScript에서 빈 객체
{}로 세면 "constructor" 같은 단어가 Object.prototype의 것과 부딪치고, "10" 같은 수 모양 열쇠는 넣은 차례와 상관없이 앞으로 정렬돼요.세기에는
Map을 쓰세요.