全局保存数组
Saving an array globally
我想要达到的目标
我有一个包含 2 张纸的电子表格 A
& B
。
A
有 2 列 - 名称、金额(主列表)
B
有 4 列 - 名称、金额、X、Y(交易列表)
Name 列 Sheet B
引用 Name 列 Sheet A
的数据。 Whenever a name is selected, I want to populate Amount column in B
with Amount in column of sheet A
as a placeholder which users can override.为此,我计划将 Sheet A
数据加载到一个数组(全局可用)中,以便在 onEdit(e)
中我可以引用该数组而不是访问 Sheet B
.
但我能找到的选项 - CacheService
和 PropertyService
- 仅保存字符串值。但我想要:
var myGlobalArray = [];
function on init(){
//iterate and fill the array such that it has following output
//myGlobalArray[Name1] = 1
//myGlobalArray[Name2] = 2
//myGlobalArray[Name3] = 3
}
function onEdit(e){
//if selected value is name1, populate myGolbalArray[Name1] value in Amount
}
问题
在哪里以及如何定义 myGlobalArray?
我尝试将缓存服务与 JSON.Stringify
和 JSON.parse
一起使用,但数组在 onEdit
中为空。
CacheService 和 PropertyService 确实只保存字符串值,但您可以使用 JSON
utilities JSON.stringify()
and JSON.parse()
.
存储任何标量数据
// Save an array in cache service
CacheService.getPublicCache()
.put("myGlobalArray", JSON.stringify(myGlobalArray));
// Retrieve an array from property service
var myGlobalArray = JSON.parse( CacheService.getPublicCache()
.get("myGlobalArray") );
// Save an array in property service
PropertiesService.getDocumentProperties()
.setProperty("myGlobalArray", JSON.stringify(myGlobalArray));
// Retrieve an array from property service
var myGlobalArray = JSON.parse( PropertiesService.getDocumentProperties()
.getProperty("myGlobalArray") );
当调用变量 "Global" 时,我们指的是它的 范围 ,表示它可用于同一模块中的所有代码。 (您可以在 What is the scope of variables in JavaScript? 中阅读有关范围的更多信息)
但是由于您正在查看 CacheService 和 PropertyService,您已经知道范围只是问题的一部分。每次调用 onEdit()
时,它将在 Google 的其中一台服务器上的新执行实例中 运行。此新实例将无法使用先前实例中全局变量中的值。因此,我们需要在每次新调用脚本时填充 "global variable"。
引用全局变量的一种优雅方法是作为特殊 this
对象的名称属性。比如我们脚本中的每个函数都可以引用this.myGlobalArray
.1
例如,您可以将 Class Cache 文档中的 getRssFeed()
示例改编成 get_myGlobalArray()
。然后你的 onEdit()
触发器只需要首先调用它来确保 this.myGlobalArray
包含相关的数组数据。
function onEdit(e){
get_myGlobalArray();
//if selected value is name1, populate myGlobalArray[Name1] value in Amount
...
sheet.getRange(e.range.getRow(),2).setValue(myGlobalArray[e.value]);
}
/**
* Ensure the global variable "myGlobalArray" is defined and contains the
* values of column A in SheetA as an array.
*/
function get_myGlobalArray() {
if (typeof this.myGlobalArray == 'undefined') {
// Global variable doesn't exist, so need to populate it
// First, check for cached value
var cache = CacheService.getPublicCache();
var cached = cache.get("myGlobalArray");
if (cached) {
// We have a cached value, so parse it and store in global
this.myGlobalArray = JSON.parse(cached);
}
else {
// No value in the cache, so load it from spreadsheet
var data = SpreadsheetApp.getActive().getSheetByName("Sheet A").getDataRange().getValues();
this.myGlobalArray = {};
for (var row=0; row<data.length; row++) {
this.myGlobalArray[data[row][0]] = data[row][6];
}
// Stringify and store the global into the cache
cache.put("myGlobalArray", JSON.stringify(this.myGlobalArray));
}
}
}
编辑:关联数组
onEdit()
内的评论中表示:
//if selected value is name1, populate myGolbalArray[Name1] value in Amount
这意味着 myGlobalArray
是一个 关联数组 ,其中索引是一个非整数值。此要求现在反映在从电子表格读取时 this.myGlobalArray
的填充方式。
for (var row=0; row<data.length; row++) {
this.myGlobalArray[data[row][0]] = data[row][6];
// ^^^^^^^^^^^^ ^^^^^^^^^^^^
// Name ---------------/ /
// Amount ------------------------/
}
关于 Javascript 数组的不同风格已经写了很多,例如 Javascript Associative Arrays Demystified。
1 实际上,只有具有全局范围的函数才能将 this
理解为 "global to the script"。包含在对象内部的函数会将 this
解释为仅表示它们的宿主对象。但这是另一天的故事。
每次调用您的脚本都会创建一个新的脚本实例,它有自己独特的全局变量。每次调用脚本时,您实际上都会找到该特定实例的全局 "this"。您将 PropertyService 视为保存数据的持久方式是正确的。
马上我看到你的 globalArray 设置不正确:
var myGlobalArray = [];
需要
var myGlobalArray = {};
myGlobalArray['name1'] = 1
myGlobalArray['name2'] = 2
myGlobalArray['name3'] = 3
//myGlobalArray = {name3=3.0, name1=1.0, name2=2.0}
var stringArray = JSON.stringify(myGlobalArray)
//{"name1":1,"name2":2,"name3":3};
现在可以将其保存到 属性 商店并从中读取。
PropertiesService.getScriptProperties().setProperty("NameArray", stringArray);
stringArray = PropertiesService.getScriptProperties().getProperty("NameArray");
myGlobalArray = JSON.parse(stringArray);
Logger.log(myGlobalArray['name1']) // returns 1
我想要达到的目标
我有一个包含 2 张纸的电子表格 A
& B
。
A
有 2 列 - 名称、金额(主列表)B
有 4 列 - 名称、金额、X、Y(交易列表)
Name 列 Sheet B
引用 Name 列 Sheet A
的数据。 Whenever a name is selected, I want to populate Amount column in B
with Amount in column of sheet A
as a placeholder which users can override.为此,我计划将 Sheet A
数据加载到一个数组(全局可用)中,以便在 onEdit(e)
中我可以引用该数组而不是访问 Sheet B
.
但我能找到的选项 - CacheService
和 PropertyService
- 仅保存字符串值。但我想要:
var myGlobalArray = [];
function on init(){
//iterate and fill the array such that it has following output
//myGlobalArray[Name1] = 1
//myGlobalArray[Name2] = 2
//myGlobalArray[Name3] = 3
}
function onEdit(e){
//if selected value is name1, populate myGolbalArray[Name1] value in Amount
}
问题 在哪里以及如何定义 myGlobalArray?
我尝试将缓存服务与 JSON.Stringify
和 JSON.parse
一起使用,但数组在 onEdit
中为空。
CacheService 和 PropertyService 确实只保存字符串值,但您可以使用 JSON
utilities JSON.stringify()
and JSON.parse()
.
// Save an array in cache service
CacheService.getPublicCache()
.put("myGlobalArray", JSON.stringify(myGlobalArray));
// Retrieve an array from property service
var myGlobalArray = JSON.parse( CacheService.getPublicCache()
.get("myGlobalArray") );
// Save an array in property service
PropertiesService.getDocumentProperties()
.setProperty("myGlobalArray", JSON.stringify(myGlobalArray));
// Retrieve an array from property service
var myGlobalArray = JSON.parse( PropertiesService.getDocumentProperties()
.getProperty("myGlobalArray") );
当调用变量 "Global" 时,我们指的是它的 范围 ,表示它可用于同一模块中的所有代码。 (您可以在 What is the scope of variables in JavaScript? 中阅读有关范围的更多信息)
但是由于您正在查看 CacheService 和 PropertyService,您已经知道范围只是问题的一部分。每次调用 onEdit()
时,它将在 Google 的其中一台服务器上的新执行实例中 运行。此新实例将无法使用先前实例中全局变量中的值。因此,我们需要在每次新调用脚本时填充 "global variable"。
引用全局变量的一种优雅方法是作为特殊 this
对象的名称属性。比如我们脚本中的每个函数都可以引用this.myGlobalArray
.1
例如,您可以将 Class Cache 文档中的 getRssFeed()
示例改编成 get_myGlobalArray()
。然后你的 onEdit()
触发器只需要首先调用它来确保 this.myGlobalArray
包含相关的数组数据。
function onEdit(e){
get_myGlobalArray();
//if selected value is name1, populate myGlobalArray[Name1] value in Amount
...
sheet.getRange(e.range.getRow(),2).setValue(myGlobalArray[e.value]);
}
/**
* Ensure the global variable "myGlobalArray" is defined and contains the
* values of column A in SheetA as an array.
*/
function get_myGlobalArray() {
if (typeof this.myGlobalArray == 'undefined') {
// Global variable doesn't exist, so need to populate it
// First, check for cached value
var cache = CacheService.getPublicCache();
var cached = cache.get("myGlobalArray");
if (cached) {
// We have a cached value, so parse it and store in global
this.myGlobalArray = JSON.parse(cached);
}
else {
// No value in the cache, so load it from spreadsheet
var data = SpreadsheetApp.getActive().getSheetByName("Sheet A").getDataRange().getValues();
this.myGlobalArray = {};
for (var row=0; row<data.length; row++) {
this.myGlobalArray[data[row][0]] = data[row][6];
}
// Stringify and store the global into the cache
cache.put("myGlobalArray", JSON.stringify(this.myGlobalArray));
}
}
}
编辑:关联数组
onEdit()
内的评论中表示:
//if selected value is name1, populate myGolbalArray[Name1] value in Amount
这意味着 myGlobalArray
是一个 关联数组 ,其中索引是一个非整数值。此要求现在反映在从电子表格读取时 this.myGlobalArray
的填充方式。
for (var row=0; row<data.length; row++) {
this.myGlobalArray[data[row][0]] = data[row][6];
// ^^^^^^^^^^^^ ^^^^^^^^^^^^
// Name ---------------/ /
// Amount ------------------------/
}
关于 Javascript 数组的不同风格已经写了很多,例如 Javascript Associative Arrays Demystified。
1 实际上,只有具有全局范围的函数才能将 this
理解为 "global to the script"。包含在对象内部的函数会将 this
解释为仅表示它们的宿主对象。但这是另一天的故事。
每次调用您的脚本都会创建一个新的脚本实例,它有自己独特的全局变量。每次调用脚本时,您实际上都会找到该特定实例的全局 "this"。您将 PropertyService 视为保存数据的持久方式是正确的。
马上我看到你的 globalArray 设置不正确:
var myGlobalArray = [];
需要
var myGlobalArray = {};
myGlobalArray['name1'] = 1
myGlobalArray['name2'] = 2
myGlobalArray['name3'] = 3
//myGlobalArray = {name3=3.0, name1=1.0, name2=2.0}
var stringArray = JSON.stringify(myGlobalArray)
//{"name1":1,"name2":2,"name3":3};
现在可以将其保存到 属性 商店并从中读取。
PropertiesService.getScriptProperties().setProperty("NameArray", stringArray);
stringArray = PropertiesService.getScriptProperties().getProperty("NameArray");
myGlobalArray = JSON.parse(stringArray);
Logger.log(myGlobalArray['name1']) // returns 1