学习 › JavaScript
进阶 · 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 — 只是读了一下,就新建了值为 0 的 "grape" 条目
检查请用 counts.contains(w)(C++20)或 counts.find(w)。
直接读取不存在的键Python · C# · Craykon
counts = {"apple": 3}
print(counts["grape"])→ 报 KeyError: 'grape' 并停止(C# 报 KeyNotFoundException,Craykon 提示字典里没有 "grape" 这个键)
先检查,或者像 Python 的 get(w, 0) 那样给默认值。
以为会按插入顺序输出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)。
各语言的差异
在 JavaScript 里用空对象
计数请用
在 JavaScript 里用空对象
{} 计数,"constructor" 这样的单词会和 Object.prototype 上的属性冲突,而 "10" 这样像数字的键不管插入顺序如何都会按升序排在最前面。计数请用
Map。