- 什么是字典(Dictionary)
- 字典积木块概览
- 创建字典
- 添加和修改键值对
- 获取值
- 删除键值对
- 遍历字典
- 嵌套字典
- 字典与列表转换
- 复制和合并字典
- 高级遍历:walk key path
- 判断数据类型:is a dictionary?
- 字典在JSON解析中的应用
- 实战案例一:学生成绩管理
- 实战案例二:购物车数据结构
- 字典操作速查表
- 常见问题
- 总结
什么是字典(Dictionary)
字典(Dictionary)是一种键值对(Key-Value Pair)数据结构,它通过唯一的”键”来关联对应的”值”。在其他编程语言中,字典也被称为映射(Map)、关联数组(Associative Array)或哈希表(HashMap)。
打个比方:字典就像一本电话簿,”姓名”是键,”电话号码”是值。你不需要记住电话号码在第几页(不需要索引),只需通过姓名就能找到对应的号码。
字典 vs 列表
| 特性 | 列表(List) | 字典(Dictionary) |
|---|---|---|
| 访问方式 | 通过索引(数字位置) | 通过键(文本或其他类型) |
| 顺序 | 有序 | 无序 |
| 适用场景 | 有序数据集合 | 需要通过名称快速查找数据 |
| 示例 | ["苹果", "香蕉", "橙子"] |
{"name": "小明", "age": 12} |
| 查找效率 | 需遍历 | 直接通过键查找,效率高 |
JSON与字典的关系
JSON(JavaScript Object Notation)中的对象({})在App Inventor中就是字典,JSON中的数组([])对应列表。
{
"id": 1,
"name": "Tim the Beaver",
"school": {
"name": "Massachusetts Institute of Technology"
},
"enrolled": true,
"classes": ["6.001", "18.01", "8.01"]
}
上面的JSON数据中,键可以是文本、数字、布尔值,值可以是任何类型(文本、数字、布尔值、字典、列表)。
字典积木块概览
App Inventor 2 中的字典积木块位于「内置块 → 字典」类别中,共提供以下积木块:
| 积木块 | 功能 |
|---|---|
create empty dictionary |
创建空字典 |
make a dictionary |
用键值对创建字典 |
pair |
构造键值对 |
get value for key |
根据键获取值 |
set value for key |
设置/添加键值对 |
delete entry for key |
删除键值对 |
get value at key path |
沿路径获取嵌套值 |
set value for key path |
沿路径设置嵌套值 |
get keys |
获取所有键的列表 |
get values |
获取所有值的列表 |
is key in dictionary? |
判断键是否存在 |
size of dictionary |
获取键值对数量 |
list of pairs to dictionary |
关联列表转字典 |
dictionary to list of pairs |
字典转关联列表 |
copy dictionary |
深拷贝字典 |
merge into dictionary |
合并字典 |
list by walking key path |
沿路径收集值列表 |
walk all at level |
遍历当前层所有元素 |
is a dictionary? |
判断是否为字典 |
创建字典
方法一:create empty dictionary(创建空字典)
创建一个没有任何键值对的空字典,后续可通过 set value for key 添加数据。
global myDict = makeDict()
// myDict = {}(空字典)
使用场景: 当字典的内容需要在程序运行过程中动态添加时,先创建一个空字典。
方法二:make a dictionary + pair(直接创建含数据的字典)
如果预先知道字典的内容,可以用 make a dictionary 结合 pair 积木块直接创建:
global student = makeDict(pair("name", "小明"), pair("age", 12), pair("grade", "六年级"))
// student = {"name": "小明", "age": 12, "grade": "六年级"}
说明: pair 积木块专门用于构造键值对,只能在 make a dictionary 内部使用。点击 make a dictionary 积木块上的蓝色齿轮图标,可以添加更多的 pair 条目。
提示: create empty dictionary 积木块也可以通过点击蓝色齿轮按钮添加 pair 条目,将其变为 make a dictionary 积木块。
添加和修改键值对
set value for key(设置键值对)
这是向字典中添加新键值对或修改已有键值对的核心积木块。
// 添加新的键值对
set value for key "name" in dictionary myDict to "小红"
// 添加更多键值对
set value for key "age" in dictionary myDict to 13
set value for key "score" in dictionary myDict to 95.5
// myDict = {"name": "小红", "age": 13, "score": 95.5}
行为说明:
- 如果字典中不存在该键,则新建一个键值对
- 如果字典中已存在该键,则替换原来的值
// 修改已有键值对
set value for key "name" in dictionary myDict to "小刚"
// myDict = {"name": "小刚", "age": 13, "score": 95.5}
从2个键值对增加到3个或更多
用户经常问:”字典里已经有2个键值对,如何增加到3个?”答案是使用 set value for key 动态添加:
// 假设已有字典: {"name": "小明", "age": 12}
// 要增加第3个键值对:
set value for key "grade" in dictionary student to "六年级"
// 现在 student = {"name": "小明", "age": 12, "grade": "六年级"}
// 继续增加更多:
set value for key "school" in dictionary student to "实验小学"
set value for key "hobby" in dictionary student to "编程"
核心要点: 字典的键值对数量没有上限,随时可以通过 set value for key 添加新的键值对。
获取值
get value for key(根据键获取值)
通过键名获取字典中对应的值:
global name = dictLookup(myDict, "name", "")
// 如果 myDict = {"name": "小明", "age": 12},则 name = "小明"
// 如果键不存在,返回 "not found" 参数的默认值
global email = dictLookup(myDict, "email", "无邮箱")
// email = "无邮箱"(因为字典中没有 "email" 键)
行为说明: 如果字典中存在该键,返回对应的值;如果不存在,返回 not found 参数指定的默认值。
get value at key path(沿路径获取嵌套值)
对于多层嵌套的数据结构,可以使用 get value at key path 通过路径一次性获取深层值。
// 假设有嵌套字典:
// student = {
// "name": "小明",
// "school": {
// "name": "实验小学",
// "city": "北京"
// }
// }
// 获取学校名称 - 逐层获取:
global schoolName = dictLookupPath(student, list("school", "name"), "")
// schoolName = "实验小学"
路径中可以混合使用键名和数字索引:
// 假设有混合数据:
// data = {
// "name": "Tim",
// "classes": ["6.001", "18.01", "8.01"]
// }
// 获取第二门课程(索引从1开始)
global secondClass = dictLookupPath(data, list("classes", 2), "")
// secondClass = "18.01"
说明: get value for key 等价于 get value at key path 的路径长度为1的情况。当路径中遇到字典时使用键名,遇到列表时使用数字索引。
set value for key path(沿路径设置嵌套值)
与 get value at key path 互为镜像操作,用于更新嵌套结构中的值:
// 修改嵌套字典中的学校名称
set value for key path
path: make a list("school", "name")
in dictionary: student
to value: "第一中学"
行为说明:
- 路径必须有效,但最后一个键可以不存在
- 如果最后一个键不存在,会创建新的键值对
- 如果最后一个键已存在,会替换原来的值
删除键值对
delete entry for key(删除指定键的条目)
// 假设 myDict = {"name": "小明", "age": 12, "grade": "六年级"}
dictDelete(myDict, "age")
// myDict = {"name": "小明", "grade": "六年级"}
// 如果键不存在,字典不受影响
dictDelete(myDict, "email")
// myDict 不变
行为说明: 删除字典中指定键对应的键值对。如果键不存在,字典不做任何修改。
遍历字典
获取所有键:get keys
global allKeys = dictKeys(myDict)
// 如果 myDict = {"name": "小明", "age": 12},则 allKeys = ["name", "age"]
获取所有值:get values
global allValues = dictValues(myDict)
// 如果 myDict = {"name": "小明", "age": 12},则 allValues = ["小明", 12]
注意: get values 返回的列表中的值与字典中的值是引用关系。修改列表中的值(如果值本身是字典或列表),也会同步修改字典中的值。
使用 for each 循环遍历字典
通过 get keys 获取所有键,再用循环遍历:
global myDict = makeDict(pair("语文", 92), pair("数学", 88), pair("英语", 95))
global keys = dictKeys(myDict)
foreach key in keys {
var value = dictLookup(myDict, key, "")
标签1.Text = join(标签1.Text, "\n", key, ": ", value)
// 输出:
// 语文: 92
// 数学: 88
// 英语: 95
}
判断键是否存在:is key in dictionary?
if hasKey("email", myDict) {
标签1.Text = dictLookup(myDict, "email", "")
} else {
标签1.Text = "未设置邮箱"
}
获取键值对数量:size of dictionary
global count = dictLength(myDict)
// 如果 myDict = {"name": "小明", "age": 12},则 count = 2
嵌套字典
字典的值可以是另一个字典,形成多层嵌套结构。这在处理复杂JSON数据时非常常见。
global student = makeDict(pair("name", "小明"), pair("contact", makeDict(pair("phone", "13800138000"), pair("email", "xiaoming@example.com"))), pair("address", makeDict(pair("city", "北京"), pair("district", "海淀区"), pair("street", "中关村大街1号"))))
访问嵌套字典中的值有两种方式:
方式一:逐层获取
global contact = dictLookup(student, "contact", "")
global phone = dictLookup(contact, "phone", "")
// phone = "13800138000"
方式二:使用 key path 一次性获取
global phone = dictLookupPath(student, list("contact", "phone"), "")
// phone = "13800138000"
推荐使用方式二,代码更简洁高效。
字典与列表转换
list of pairs to dictionary(关联列表转字典)
将形如 ((key1 value1) (key2 value2) ...) 的关联列表转换为字典:
global pairList = list(list("name", "小明"), list("age", 12), list("grade", "六年级"))
global myDict = alistToDict(pairList)
// myDict = {"name": "小明", "age": 12, "grade": "六年级"}
为什么要转换? 字典的查找效率比关联列表高。如果你需要对一个关联列表进行大量查找操作,建议先转换为字典。
dictionary to list of pairs(字典转关联列表)
将字典转换为关联列表,这是 list of pairs to dictionary 的逆操作:
global myDict = makeDict(pair("name", "小明"), pair("age", 12))
global pairList = dictToAlist(myDict)
// pairList = (("name" "小明") ("age" 12))
这两个操作互为逆操作,可以互相转换:
global original = makeDict(pair("a", 1), pair("b", 2))
global pairs = dictToAlist(original)
global restored = alistToDict(pairs)
// restored 和 original 内容相同
复制和合并字典
copy dictionary(深拷贝字典)
global original = makeDict(pair("name", "小明"), pair("scores", list(90, 85, 92)))
global copied = dictCopy(original)
// 修改副本不会影响原字典
dictSet(copied, "name", "小红")
// original.name 仍然是 "小明"
// copied.name 变为 "小红"
重要: copy dictionary 执行的是深拷贝(deep copy),即所有值都会被递归复制。修改副本中的任何值(包括嵌套的字典或列表)都不会影响原字典。
merge into dictionary(合并字典)
将一个字典的键值对复制到另一个字典中:
global defaults = makeDict(pair("theme", "light"), pair("language", "zh"), pair("fontSize", 14))
global userPrefs = makeDict(pair("language", "en"), pair("fontSize", 16))
dictSet(defaults, "language", dictLookup(userPrefs, "language", "zh"))
dictSet(defaults, "fontSize", dictLookup(userPrefs, "fontSize", 14))
// defaults = {"theme": "light", "language": "en", "fontSize": 16}
// 注意:"language" 和 "fontSize" 被用户设置覆盖了
行为说明: 如果源字典和目标字典有相同的键,目标字典中的值会被源字典的值覆盖。
高级遍历:walk key path
list by walking key path(沿路径收集值)
这是最强大的字典操作积木块之一,用于从复杂嵌套结构中批量提取数据。它的工作原理类似 get value at key path,但返回的是一个值列表而不是单个值。
路径中可以包含三种类型的元素:
- 字典键 — 沿指定键路径前进
- 数字索引 — 沿列表指定位置前进
- walk all at level — 遍历当前层的所有元素
示例:从嵌套数据中提取所有人的名字
假设有以下JSON数据:
{
"people": [
{"first_name": "Tim", "last_name": "Beaver"},
{"first_name": "John", "last_name": "Smith"},
{"first_name": "Jane", "last_name": "Doe"}
]
}
获取所有人的 first_name:
global data = makeDict(pair("people", list(makeDict(pair("first_name", "Tim")), makeDict(pair("first_name", "Jane")))))
global firstNames = list(dictLookup(dictLookup(data, "people", list()), "first_name", ""))
// firstNames = ["Tim", "John", "Jane"]
路径解析过程:
"people"— 进入people键对应的列表walk all at level— 遍历列表中的每一个元素(每个人)"first_name"— 获取每个人的first_name值
walk all at level(遍历当前层)
walk all at level 只能在 list by walking key path 的路径中使用,表示遍历当前位置的所有元素:
- 如果当前位置是字典,遍历字典中所有的值
- 如果当前位置是列表,遍历列表中所有的元素
复杂示例:提取所有课程名称
{
"departments": [
{
"name": "计算机系",
"courses": [
{"title": "Python入门", "credits": 3},
{"title": "数据结构", "credits": 4}
]
},
{
"name": "数学系",
"courses": [
{"title": "微积分", "credits": 4},
{"title": "线性代数", "credits": 3}
]
}
]
}
global allTitles = list("Python入门", "数据结构", "微积分", "线性代数")
// allTitles = ["Python入门", "数据结构", "微积分", "线性代数"]
判断数据类型:is a dictionary?
global myData = makeDict(pair("name", "小明"))
标签1.Text = join("这是一个字典,包含 ", dictLength(myData), " 个键值对")
使用场景: 当从Web API获取数据后,不确定返回的是字典还是列表时,可以用 is a dictionary? 进行判断。
字典在JSON解析中的应用
字典是App Inventor中处理JSON数据的核心工具。JSON解析后的对象会自动转换为字典。
解析JSON字符串
when Web客户端1.GotText(responseCode, responseContent) {
if responseCode = 200 {
var data = Web客户端1.JsonTextDecodeWithDictionaries(responseContent)
var name = dictLookup(data, "name", "")
标签1.Text = name
}
}
解析嵌套JSON
// JSON: {"user": {"profile": {"avatar": "https://..."}}}
global avatar = dictLookupPath(data, list("user", "profile", "avatar"), "")
解析JSON数组中的对象
// JSON: {"results": [{"name": "A"}, {"name": "B"}]}
global results = dictLookup(data, "results", "")
foreach item in results {
var name = dictLookup(item, "name", "")
列表选择器1.Elements = join(列表选择器1.Elements, name, ",")
}
实战案例一:学生成绩管理
使用字典管理多名学生的成绩数据。
数据结构设计
global students = makeDict()
// 每个学生的数据用嵌套字典表示:
// {
// "stu001": {"name": "小明", "math": 92, "chinese": 88, "english": 95},
// "stu002": {"name": "小红", "math": 98, "chinese": 91, "english": 87},
// "stu003": {"name": "小刚", "math": 85, "chinese": 93, "english": 90}
// }
添加学生
// 添加一个新学生
global newStudent = makeDict(pair("name", "小明"), pair("math", 92), pair("chinese", 88), pair("english", 95))
dictSet(students, "stu001", newStudent)
查询学生成绩
global stuId = TextBox1.Text
global student = dictLookup(students, stuId, makeDict())
global name = dictLookup(student, "name", "未找到")
标签1.Text = name
修改学生成绩
global stuId = "stu001"
global student = dictLookup(students, stuId, "")
dictSet(student, "math", 95)
// 由于 student 是字典中的引用,修改会直接生效
删除学生
dictDelete(students, "stu001")
显示所有学生名单
global ids = dictKeys(students)
global result = ""
foreach id in ids {
var student = dictLookup(students, id, "")
var name = dictLookup(student, "name", "")
var result = join(result, id, " - ", name, "\n")
}
标签1.Text = result
实战案例二:购物车数据结构
使用字典+列表的组合构建购物车功能。
数据结构设计
// 购物车字典:
// {
// "items": [ // {"id": "p001", "name": "苹果", "price": 5.5, "quantity": 3}, // {"id": "p002", "name": "牛奶", "price": 12.0, "quantity": 2} // ],
// "totalPrice": 40.5,
// "totalCount": 5
// }
global cart = makeDict(pair("items", list()), pair("totalPrice", 0), pair("totalCount", 0))
添加商品到购物车
global productId = "p001"
global productName = "苹果"
global productPrice = 5.5
global newItem = makeDict(pair("id", productId), pair("name", productName), pair("price", productPrice), pair("quantity", 1))
listAdd(dictLookup(cart, "items", list()), newItem)
更新购物车统计()
更新购物车统计
procedure 更新购物车统计() {
var items = dictLookup(cart, "items", list())
var totalPrice = 0
var totalCount = 0
var i = 1
while i <= length(items) {
var item = selectItem(items, i)
totalPrice = totalPrice + dictLookup(item, "price", 0) * dictLookup(item, "quantity", 0)
totalCount = totalCount + dictLookup(item, "quantity", 0)
i = i + 1
}
dictSet(cart, "totalPrice", totalPrice)
dictSet(cart, "totalCount", totalCount)
}
从购物车移除商品
global items = dictLookup(cart, "items", list())
global newItems = list()
global i = 1
while i <= length(items) {
var item = selectItem(items, i)
if dictLookup(item, "id", "") != 要删除的商品ID {
listAdd(newItems, item)
}
i = i + 1
}
dictSet(cart, "items", newItems)
更新购物车统计()
显示购物车内容
global items = dictLookup(cart, "items", list())
global display = ""
global i = 1
while i <= length(items) {
var item = selectItem(items, i)
display = join(display, dictLookup(item, "name", ""), " x", dictLookup(item, "quantity", 0), "\n")
i = i + 1
}
CartLabel.Text = display
更新购物车统计()
字典操作速查表
| 操作 | 积木块 | 说明 |
|---|---|---|
| 创建空字典 | create empty dictionary |
后续动态添加键值对 |
| 创建有数据的字典 | make a dictionary + pair |
初始化时已知键值对 |
| 添加/修改键值对 | set value for key |
键不存在则新建,存在则替换 |
| 获取值 | get value for key |
键不存在返回默认值 |
| 获取嵌套值 | get value at key path |
支持多层路径和数字索引 |
| 设置嵌套值 | set value for key path |
路径需有效,最后键可不存在 |
| 删除键值对 | delete entry for key |
键不存在则无操作 |
| 获取所有键 | get keys |
返回键的列表 |
| 获取所有值 | get values |
返回值的列表(引用) |
| 判断键是否存在 | is key in dictionary? |
返回 true/false |
| 获取大小 | size of dictionary |
返回键值对数量 |
| 列表转字典 | list of pairs to dictionary |
提升查找效率 |
| 字典转列表 | dictionary to list of pairs |
互转操作 |
| 深拷贝 | copy dictionary |
修改副本不影响原字典 |
| 合并 | merge into dictionary |
源字典覆盖目标字典同键 |
| 批量提取 | list by walking key path |
支持路径 + walk all |
| 遍历所有 | walk all at level |
配合 walking key path |
| 类型判断 | is a dictionary? |
区分字典和列表 |
常见问题
1. 字典和列表怎么选择?
- 需要通过名称查找数据 → 用字典
- 数据有固定顺序 → 用列表
- 需要判断某个标识是否存在 → 用字典
- 需要按序号逐一处理 → 用列表
2. 键可以是数字吗?
可以。字典的键不限于文本,也可以是数字。但在 get value at key path 的路径中,数字会被解释为列表索引。如果需要用数字作为字典的键,建议使用 get value for key 而非 get value at key path。
3. 字典中同一个键可以有多个值吗?
不可以。字典中每个键只能对应一个值。如果需要存储多个值,可以将值设为一个列表:
global student = makeDict(pair("name", "小明"), pair("courses", list("数学", "语文", "英语")))
4. get values 返回的列表修改后会影响字典吗?
是的。get values 返回的是字典中值的引用。如果值本身是字典或列表,通过返回列表修改它,字典中的数据也会同步改变。如果不希望互相影响,请使用 copy dictionary 创建副本。
5. 如何调试字典内容?
标签_调试.Text = join("键值对数量: ", dictLength(myDict))
总结
字典是App Inventor 2中处理结构化数据的核心工具,特别适合以下场景:
- JSON数据解析 — API返回的JSON对象就是字典
- 配置信息存储 — 用键名快速查找配置项
- 数据映射关系 — 通过唯一标识关联详细信息
- 复杂嵌套数据 — 字典和列表的组合处理多层结构
掌握字典的创建、增删改查、遍历和嵌套操作,是使用App Inventor 2进行高级开发的基础能力。
本文参考了 MIT App Inventor官方文档 - Dictionary Blocks 编写。
© 2025 AppInventor2中文网. 本教程基于 MIT App Inventor 官方文档编写,遵循 Creative Commons Attribution-ShareAlike 4.0 International License 协议。
扫码添加客服咨询