使用 Google Apps 脚本替换数组中的行

Replace Rows in Array using Google Apps Script

我仍在研究 Apps 脚本,只是需要一些帮助来编辑数组。

所以我有两个数组:

var arrayBig = SpreadsheetApp.getSheetByName('Big').getDataRange().getValues();

var arraySmall = SpreadsheetApp.getSheetByName('Small').getDataRange().getValues();

我想根据 arraySmall 中的值替换 arrayBig 中的特定行,然后将该数组写回我的电子表格(我同意的最后一部分)。

两个数组的列数相同,但arraySmall行数较少。

要替换的行数:

因此,如果第 1 列中的值 = 3 将 arrayBig 中的行号 3 替换为 arraySmall 中第 1 列 = 3.

中行的内容

我认为答案与 map method 有关,但我不明白。非常感谢您提供一些帮助我入门的建议,谢谢。

这是一种“入门”方法:

你描述了你的数据,但你没有提供任何具体的样本 - 所以这是我的起始数据,根据问题中的信息做出一些假设:

var arrayBig = [ [1, "A", "B"], [2, "C", "D"], [3, "E", "F"], [4, "G", "H"] ];
var arraySmall  = [ [1, "P", "Q"], [3, "Y", "Z"] ];

根据以上数据,预期结果如下,其中第 1 行和第 3 行被替换:

[ [1, "P", "Q"], [2, "C", "D"], [3, "Y", "Z"], [4, "G", "H"] ];

方法如下:

var arrayBig = [ [1, "A", "B"], [2, "C", "D"], [3, "E", "F"], [4, "G", "H"] ];
var arraySmall  = [ [1, "P", "Q"], [3, "Y", "Z"] ];

var mapper = new Map();
arraySmall.forEach((row) => { 
  mapper.set(row[0], row);
} );

newArrayBig = [];
arrayBig.forEach((row) => { 
  if ( mapper.has( row[0] ) ) {
    newArrayBig.push( mapper.get( row[0] ) );
  } else {
    newArrayBig.push( row );
  }
} );

console.log( newArrayBig );

这假设您有一个铁定的保证 arraySmall 中的数据行绝不会多于 arrayBig 中的数据行。您可以(并且应该)添加一些逻辑来对此进行测试,以获得更健壮的代码。

备注

Map 对象(不是 map() 方法)是这种方法的核心。这个对象提供了一个查找结构:一个索引值,指向一个数据值:

var mapper = new Map();
arraySmall.forEach((row) => { 
  mapper.set(row[0], row);
} );

在我们的例子中,索引是每个 arraySmall 行第一个单元格中的数字。它指向的值是该行的完整数据数组,例如:

1 -> [1, "P", "Q"]
3 -> [3, "Y", "Z"]

我们现在可以在遍历 arrayBig 数据的每一行时使用此查找数据:

arrayBig.forEach((row) => { ... } );

这个 forEach 迭代器中的逻辑基本上是这样说的:如果映射包含一个使用与当前 arrayBig 行相同编号的数组,则使用 arraysmall 数据。否则,使用 arrayBig 数据。

newArrayBig.push() 方法将该数组添加到我们的结果数组中。

然后您可以根据需要将其写回您的电子表格。

以防万一。另一个解决方案:

var arrayBig    = [ [1,"A","B"], [2,"C","D"], [3,"E","F"], [4,"G","H"] ];
var arraySmall  = [ [1,"P","Q"], [3,"Y","Z"] ];

var obj = {}; // it will be { "1":[1,"P","Q"], "3": [3,"Y","Z"] }

arraySmall.forEach( x => obj[x[0]] = x );

arrayBig = arrayBig.map( x => obj[x[0]] || x );

console.log(arrayBig); // [[1,"P","Q"], [2,"C","D"], [3,"Y","Z"], [4,"G","H"]];

它将小数组转换为一个对象,其中每行的第一个元素是一个键,该行是它的值。然后它遍历大数组并尝试通过键(第一个单元格)获取对象的值。如果键存在,行将被替换,如果键不存在,for 不会改变。

小数组中的行顺序也无关紧要。

更新

如果需要,您可以使用数组代替对象:

var arrayBig = [ [1,"A","B"], [2,"C","D"], [3,"E","F"], [4,"G","H"] ];
var arraySmall  = [ [1,"P","Q"], [3,"Y","Z"] ];

var arr = [];

arraySmall.forEach( x => arr[x[0]-1] = x ); // [ [1,"P","Q"], [], [3,"Y","Z"], [] ];

arrayBig = arrayBig.map( (x,i) => arr[i] || x);

console.log(arrayBig);

我认为这会占用更多内存,但它确实有效。但是你需要确保第一个单元格只包含数字,因为它们用作数组的索引(数组的索引应该是数字,不像键可以是任何东西)。第一个数组的行应该排序并且没有跳过的数字(1、2、3、4 等)。所以,这是一个更脆弱的解决方案。仅用于教育目的。