外观
映射
约 7097 字大约 24 分钟
高级
注意
本页面包含高级内容。如果您是 Mine Script® 编程初学者,建议先熟悉其他更基础的 Mine Script 功能,再学习本部分内容。
简介
Mine脚本映射(Map)是以键值对形式存储元素的集合类型,允许脚本通过唯一标识符(键)关联多个值引用。
与数组和矩阵不同,映射是无序集合。脚本通过直接引用存入的键值对中的键来快速访问映射值,而无需遍历内部索引。
映射的键可以是任何基本类型或枚举类型,其值可以是任何可用类型。映射不能直接使用其他集合(映射、数组或矩阵)作为值,但可以持有包含这些数据结构的用户自定义类型(UDT)实例。详见相关章节说明。
与其他集合类型相同,映射最多可包含100,000个元素。由于每个键值对包含两个元素(唯一键及其关联值),因此单个映射最多可容纳50,000个键值对。
映射声明
Mine Script采用以下语法声明映射:
[var/varip ][map<keyType, valueType> ]<identifier> = <expression>其中:
<keyType, valueType>为映射的类型模板,声明其包含的键和值类型<expression>需返回映射实例或na值
当声明赋值为 na 的映射变量时,必须包含 map 关键字及类型模板,以告知编译器该变量可接受键类型为 keyType、值类型为 valueType 的映射。
例如,以下代码声明了一个新的 myMap 变量,该变量可接受包含字符串键和浮点数值的映射实例:
Mine Script®
已复制
当 <expression> 不为 na 时,编译器无需显式类型声明,因其可从赋值的映射对象自动推断类型信息。
此代码声明了一个 myMap 变量,初始化为包含字符串键和浮点数值的空映射。后续赋值的所有映射必须保持相同的键值类型:
Mine Script®
已复制
使用 var 和 varip 关键字
用户可通过添加 var 或 varip 关键字,指示脚本仅在首个图表柱上声明映射变量。使用这些关键字的变量在每次脚本迭代时都指向同一个映射实例,直至被显式重新赋值。
例如,以下脚本在首个图表柱上声明了一个 colorMap 变量,该变量持有字符串键与颜色值的键值对。脚本在图表上显示振荡指标,并利用首个柱上存入 colorMap 的值来为所有柱上的绘图着色:

Mine Script®
已复制
注意
使用 varip 声明的映射变量在历史数据上的行为与 var 相同,但在实时柱(即自脚本最后一次编译以来的新柱)上,会在每个新价格跳动时更新其键值对。赋给 varip 变量的映射只能包含以下类型的值:
映射读写操作
存入和获取键值对
map.put()是映射用户最常使用的函数,作为向映射中存入新键值对的主要方法。该调用将键参数与值参数关联,并将该键值对添加到映射id中。
若map.put()调用中的键参数已存在于映射键中,则传入的新键值对将替换现有键值对。
要从映射id中获取与给定键关联的值,请使用map.get()。如果id映射包含该键,则此函数返回值;否则返回na。
以下示例借助map.put()和map.get()方法,计算在给定长度length内,当收盘价最后一次上涨和下跌时的bar_index差值。脚本在价格上涨时将("Rising", bar_index)键值对存入data映射,在价格下跌时将("Falling", bar_index)键值对存入映射。随后将包含"Rising"和"Falling"值之间"差值"的键值对存入映射,并在图表上绘制该值:

Mine Script®
已复制
需要注意的是:
- 本脚本在连续调用data.put()时会替换与“Rising”, “Falling”, 和 “Difference”键关联的值,因为这些键具有唯一性,在
data映射中每个键只能出现一次。 - 替换映射中的键值对不会改变其键的内部插入顺序。我们将在下一节进一步讨论这一点。
与其他集合类型类似,当向映射中存入特殊类型(线(line)、线填充(linefill)、方框(box)、多段线(polyline)、标签(label)、表格(table) 和 图表点(chart.point) )或用户自定义类型(UDT)的值时,需注意存入的键值对所引用的值是同一对象而非其副本。修改键值对所引用的值将同时影响原始对象。
例如,此脚本定义了一个包含o、h、l、c字段的自定义ChartData类型。在首个图表柱上,脚本声明了myMap变量并添加键值对("A", myData),其中myData是字段值初始为na的ChartData实例。随后添加("B", myData)到myMap,并通过用户定义的update()方法在每个柱上更新该键值对的对象。
对键"B"对应对象的每次修改都会影响键"A"引用的对象,这通过"A"对象字段绘制的蜡烛图得以展示:

Mine Script®
已复制
需要注意的是:
- 若本脚本在每次调用myMap.put()时传入
myData的副本,其行为表现将会不同。更多详细信息,请参阅用户手册中关于对象的章节。
检查键和值
map.keys() 与 map.values()
要获取映射中的所有键和值,请使用 map.keys() 和 map.values()。这些函数会将映射 id 中的所有键/值引用复制到新的数组对象中。修改这些函数返回的数组不会影响原始映射 id。
虽然映射是无序集合,但 Mine Script 内部会维护键值对的插入顺序。因此,map.keys() 和 map.values() 返回的数组元素顺序始终与映射 id 的插入顺序一致。
以下脚本通过每 50 根柱子在标签中显示映射 m 的键数组和值数组来演示这一特性。从图表中可见,m.keys() 和 m.values() 返回的数组元素顺序与映射 m 中键值对的插入顺序完全对应:

Mine Script®
已复制
需要注意的是:
- 键
"First"对应的值是0到100之间的随机整数。键"Second"对应的值比"First"的值大1,而键"Third"对应的值又比"Second"的值大1。
需要特别注意:当替换映射中的键值对时,其内部插入顺序不会改变。在此情况下,新元素在keys()和values()数组中的位置将与旧元素保持一致。唯一的例外是脚本事先完全移除了该键。
我们在下方添加了一行代码,将带有"Second"键的新值存入映射m,覆盖了该键原先关联的值。尽管脚本是在存入"Third"键对应的键值对之后才执行此操作,但由于"Second"键在修改前已存在于m中,该键值对的键和值在keys与values数组中仍保持第二位。

Mine Script®
已复制
注意
map.values() 数组中的元素与映射 id 指向相同的值。因此,当映射值为引用类型时(包括线(line)、线填充(linefill)、方框(box)、多段线(polyline)、标签(label)、表格(table) 和 图表点(chart.point) )或用户自定义类型(UDT),修改 map.values() 数组所引用的实例也会影响映射 id 引用的对象,因为这两个集合的内容都指向相同的对象。
map.contains()
要检查映射 id 中是否存在特定键,请使用 map.contains()。此函数可作为 array.includes() 的便捷替代方案,无需先通过 map.keys() 获取键数组再进行检查。
例如,以下脚本检查映射 m 中是否存在多个指定键,并将检查结果显示在标签中:

Mine Script®
已复制
删除键值对
要从映射id中移除特定键值对,请使用map.remove()。该函数会从映射中移除键及其关联值,同时保留其他键值对的插入顺序。如果映射包含该键,则返回被移除的值;否则返回na。
要一次性移除映射id中的所有键值对,请使用map.clear()。
以下脚本创建新的m映射,向映射中存入键值对,在循环中使用m.remove()移除removeKeys数组中列出的每个有效键,然后调用m.clear()移除所有剩余键值对。脚本使用自定义debugLabel()方法在每次更改后显示m的大小、键和值:

Mine Script®
已复制
需要注意的是:
removeKeys数组中的字符串并非全部存在于映射m的键中。尝试移除不存在的键(本例中的"F"、"a"和第二个"B")不会对映射内容产生任何影响。
映射合并
脚本可以通过map.put_all()合并两个映射。该函数按照插入顺序将id2映射中的所有键值对存入id1映射。与map.put()类似,如果id2中的任何键也存在于id1中,此函数会替换包含这些键的键值对,但不会影响它们最初的插入顺序。
此示例包含一个用户自定义的hexMap()函数,该函数将十进制整数键映射为其十六进制形式的字符串表示。脚本使用此函数创建两个映射mapA和mapB,然后使用mapA.put_all(mapB)将mapB中的所有键值对存入mapA。
脚本使用自定义debugLabel()函数显示包含mapA和mapB键值的标签,以及另一个显示将mapB所有键值对存入mapA后其内容的标签:

Mine Script®
已复制
映射遍历
脚本可通过多种方式迭代访问映射中的键和值。例如,可以遍历映射的 keys() 数组并通过 get() 获取每个键对应的值,如下所示:
Mine Script®
已复制
但更推荐直接在映射上使用 for...in 循环,该方式会按照键值对的插入顺序进行遍历,每次迭代返回包含下一个键值对的元组(键和值)。
例如,以下代码行遍历 thisMap 中的每个键和值,从最先插入的键值对开始:
Mine Script®
已复制
让我们使用这种结构编写一个在表格中显示映射键值对的脚本。在以下示例中,我们定义了一个自定义的 toTable() 方法,该方法会创建一个表格,然后使用 for...in 循环遍历映射的键值对并填充表格单元格。脚本使用此方法可视化一个包含价格和成交量数据 length-bar 平均值的映射:

Mine Script®
已复制
复制映射
浅拷贝
脚本可以使用map.copy()函数创建映射id的浅拷贝。对浅拷贝的修改不会影响原始id映射或其内部插入顺序。
例如,该脚本构建了一个m映射,其键"A"、"B"、"C"和"D"被分配了0到10之间的四个随机值。然后创建m映射的浅拷贝mCopy,并更新与其键关联的值。脚本使用自定义debugLabel()方法在图表上显示m和mCopy中的键值对:

Mine Script®
已复制
深拷贝
虽然当复制包含基本类型或枚举类型值的映射时,浅拷贝已足够,但必须理解的是:持有特殊类型值(线(line)、线填充(linefill)、方框(box)、多段线(polyline)、标签(label)、表格(table) 和 图表点(chart.point) )或用户自定义类型(UDT)的映射浅拷贝会指向与原始映射相同的对象。修改浅拷贝引用的对象将影响原始映射引用的实例,反之亦然。
为确保对复制映射所引用对象的修改不会影响其他位置的实例,可以通过创建新映射来实现深拷贝,新映射包含原始映射中每个值的副本键值对。
此示例创建了一个字符串键和标签值的原始映射,并向其中存入一个键值对。脚本通过内置copy()方法将映射复制到浅拷贝变量,然后使用自定义deepCopy()方法复制到深拷贝变量。
从图表可见,对浅拷贝获取的标签所做的修改也会影响原始映射引用的实例,但对深拷贝的修改则不会:

Mine Script®
已复制
需要注意的是:
deepCopy()方法会遍历原始(original)映射,复制每个值,并将包含这些副本的键值对存入新的映射实例中。
作用域与历史记录
与Mine中的其他集合类型一样,映射变量会在每个柱上留下历史记录,允许脚本使用历史引用操作符[]访问之前分配给变量的映射实例。脚本还可以将映射分配给全局变量,并在函数、方法和条件结构的作用域内与之交互。
例如,该脚本使用全局映射及其历史记录来计算一组EMA聚合值。它声明了一个int键和float值的globalData映射,其中映射中的每个键对应于每个EMA计算的长度。用户定义的update()函数通过将分配给globalData的前一个映射的值与当前源值混合来计算每个键长度的EMA。
脚本绘制了全局映射values()数组中的最大值和最小值,以及globalData.get(50)的值(即50周期EMA):

Mine Script®
已复制
其他集合的映射
映射不能直接使用其他映射、数组或矩阵作为值,但可以存储包含这些集合类型的用户自定义类型(UDT)实例作为值。
例如,假设我们需要创建一个"二维"映射,使用字符串键访问嵌套的<string, float>映射。由于映射不能直接以其他集合为值,我们需先创建包装类型,其字段用于存储map<string, float>实例,如下所示:
Mine Script®
已复制
定义好 Wrapper 类型后,我们就可以创建以字符串为键、Wrapper 类型为值的映射了。该映射中每个值的 data 字段都指向一个 map<string, float> 实例:
Mine Script®
已复制
以下脚本利用这一概念构建了一个包含从多个交易品种请求的OHLCV数据的映射结构。用户自定义的requestData()函数从交易品种请求价格和成交量数据,创建一个<string, float>映射,将数据存入其中,然后返回包含该新映射的Wrapper实例。
脚本将每次调用requestData()的结果存入mapOfMaps映射,然后使用用户自定义的toString()方法创建嵌套映射的字符串表示,最终在图表的标签中显示:

Mine Script®
已复制