App Inventor 2 字典(Dictionary)操作教程 - 键值对数据结构

« 返回首页

什么是字典(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,但返回的是一个值列表而不是单个值。

路径中可以包含三种类型的元素:

  1. 字典键 — 沿指定键路径前进
  2. 数字索引 — 沿列表指定位置前进
  3. 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"]

路径解析过程:

  1. "people" — 进入 people 键对应的列表
  2. walk all at level — 遍历列表中的每一个元素(每个人)
  3. "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中处理结构化数据的核心工具,特别适合以下场景:

  1. JSON数据解析 — API返回的JSON对象就是字典
  2. 配置信息存储 — 用键名快速查找配置项
  3. 数据映射关系 — 通过唯一标识关联详细信息
  4. 复杂嵌套数据 — 字典和列表的组合处理多层结构

掌握字典的创建、增删改查、遍历和嵌套操作,是使用App Inventor 2进行高级开发的基础能力。


本文参考了 MIT App Inventor官方文档 - Dictionary Blocks 编写。


© 2025 AppInventor2中文网. 本教程基于 MIT App Inventor 官方文档编写,遵循 Creative Commons Attribution-ShareAlike 4.0 International License 协议。

文档反馈