遍历范围,将字符串附加到每个

Posted

技术标签:

【中文标题】遍历范围,将字符串附加到每个【英文标题】:Iterate over range, append string to each 【发布时间】:2012-11-16 07:29:54 【问题描述】:

我在 Google 表格 (activerange) 中选择了一系列单元格。我想遍历该范围内的每个单元格,并在末尾添加一个字符串。字符串始终相同,并且可以硬编码到函数中。

这似乎是一件非常简单的事情,但我已经搞砸了一个小时的代码,无法让任何有用的事情发生,而且文档真的没有帮助。

这就是我现在所拥有的。我不会编写 JS 代码(我知道 VBA,这对我有帮助..)。

function appendString() 
  var range = SpreadsheetApp.getActiveSheet().getActiveRange();
  for (var i = 0; i < range.length; i++) 
    var currentValue = range[i].getValue();
    var withString = currentValue + " string";
    range[i].setValue(withString);
  

【问题讨论】:

【参考方案1】:

你可以试试这样的:

//
// helper `forEachRangeCell` function
//

function forEachRangeCell(range, f) 
  const numRows = range.getNumRows();
  const numCols = range.getNumColumns();

  for (let i = 1; i <= numCols; i++) 
    for (let j = 1; j <= numRows; j++) 
      const cell = range.getCell(j, i)

      f(cell)
    
  


//
// Usage
//

const range = SpreadsheetApp.getActiveSheet().getActiveRange();

forEachRangeCell(range, (cell) => 
  cell.setValue(`$cell.getValue() string`)
)

【讨论】:

【参考方案2】:

或者使用setValues() 同时写入所有值。似乎也执行得更快。

var range = SpreadsheetApp.getActiveSheet().getActiveRange();
var numRows = range.getNumRows();
var numCols = range.getNumColumns();
var writeValues = []
for (var i = 1; i <= numRows; i++) 
  var row = []
  for (var j = 1; j <= numCols; j++) 
    var currentValue = range.getCell(i,j).getValue();
    var withString = currentValue + " string";
    row.push(withString)
  
  writeValues.push(row)

range.setValues(writeValues)

【讨论】:

是的,这会快得多,尤其是在处理大范围时,因为更新单元格的过程非常缓慢(因为它每次都会更新 UI)。如果您正在处理大量单元格,则接受的答案甚至不会起作用,因为它会超时。【参考方案3】:

2020 年 3 月编辑:您现在可以使用现代 ECMAScript。 If you enable the V8 runtime,这行得通:

function appendString() 
  const range = SpreadsheetApp.getActiveSheet().getActiveRange();
  const values = range.getValues();
  const modified = values.map(row => row.map(currentValue => currentValue + " string"));
  range.setValues(modified);

如果您必须使用较旧的 javascript 版本,您可以:

function appendString() 
    var range = SpreadsheetApp.getActiveSheet().getActiveRange();
    var values = range.getValues();

    values.forEach(function(row, rowId) 
        row.forEach(function(col, colId) 
            values[rowId][colId] += " string";
        );
    );

    range.setValues(values);

请注意,rowIdcolId 从零开始。在接受的答案中,索引是基于 1 的。或者您使用 map 而不使用箭头运算符:

function appendString() 
  var range = SpreadsheetApp.getActiveSheet().getActiveRange();
  var values = range.getValues();

  var modified = values.map(function (row) 
    return row.map(function (col)  
      return col + " string"; 
    ); 
  )

  range.setValues(modified);

【讨论】:

map 在这种情况下似乎更合适:range.setValues(values.map(function (row) return row.map(function (val) return val + theConstantValue; ); )) @tehhowch 您的解决方案有点难以理解。但它有效。而且很好。也许我应该添加它作为我的答案的替代品。 如果 Apps 脚本有箭头语法,它可能更容易理解,没有函数的样板和返回关键字:range.setValues(values.map(row =&gt; row.map(val =&gt; val + theConstantValue))); @tehhowch:我认为,您的解决方案是最好的。我将其添加到我的答案中。谢谢。【参考方案4】:

这里是Voy's post 的更新,使用range.getValues() 获取所有值并省略临时数组。应该更快,因为在二维循环中省略了range.getCell().getValue()。请注意,此 sn-p 中的索引从 0 开始。我也觉得这更具可读性。

  var cells = range.getValues();
  var numRows = range.getNumRows();
  var numCols = range.getNumColumns();
  for (var i = 0; i < numRows; i++) 
    for (var j = 0; j < numCols; j++) 
      cells[i][j] += " string";
    
  

  range.setValues(cells);

【讨论】:

【参考方案5】:

这是一个非常通用的函数,它迭代一个范围的值。它也可以用来对它执行reduce 函数(这对您的情况很有用)。如果您只想找到元素的第一个,它也可以跳出循环。

可以很容易地更改为接受实际的 Range 实例而不是值数组。

function range_reduce(rangeValues,fn,collection) 
  collection = collection || [];
  var debug_rr = "<<";
  for(var rowIndex = 0, row=undefined; rowIndex<rangeValues.length && (row = rangeValues[rowIndex]); rowIndex++)  
    for(var colIndex = 0, value=undefined; colIndex<row.length && (value = row[colIndex]); colIndex++) 
      try 
        collection = fn(collection, value, rowIndex, colIndex);
       catch (e) 
        if(! e instanceof BreakException) 
          throw e;
         else 
          return collection;
        
      
    
  
  return collection;


// this is a created, arbitrary function to serve as a way
// to break out of the reduce function. Your callback would
// `throw new BreakException()` and `rang_reduce` would stop
// there and not continue iterating over "rangeValues".
function BreakException();

在你的情况下:

var range = SpreadsheetApp.getActiveSheet().getActiveRange()
var writeValues = range_reduce(range.getValues(), function(collection, value, row, col) 
    collection[row] || collection.push([]);
    collection[row].push(value + " string");
);
range.setValues(writeValues)

【讨论】:

在哪里可以找到 BreakException 的定义? 大声笑,好点@trex005。我在答案中添加了定义和描述。【参考方案6】:

google-sheets 您可以使用查找和替换轻松完成此操作。

选择您的范围

查找:

^(.*)$

替换:

$1AppendString

标记使用正则表达式

点击全部替换

我看不出在这里使用脚本有什么好处,但是,如果必须,您也可以通过工作表 API 发出 Find Replace 请求。

【讨论】:

【参考方案7】:

Google 表格使用多维数组,因此为了让您的生活更轻松,您可以像这样展平数组:

range.getValues().flat().forEach(function(item, i)
    var currentValue = item[i].getValue();
    var withString = currentValue + " string";
    item[i].setValue(withString);
);

【讨论】:

【参考方案8】:

这就是我会这样做的方式。它有点长,但我认为它非常实用且可重复使用。绝对实用。

这使用了 V8 引擎和 TypeScript

/*
    Transforms the original "Array of Arrays"—
    [
        [a, b, c, d, e],
        [a, b, c, d, e],
        [...],
        ...,
    ]
  
    into an "Array of Objects".
    [
        timestamp: a, email: b, response_1: c, response_2: d, response_3: e,
        timestamp: a, email: b, response_1: c, response_2: d, response_3: e,
        ...,
        ...,
    ]
*/
var original_values = SpreadsheetApp.getActiveSheet()
  .getRange("A:E")
  .getValues()
  .map(
    ([
      a, b, c, d, e,
   // f, g, h, i, j,
   // k, l, m, n, o,
   // p, q, r, s, t,
   // u, v, w, x, y,
   // z, aa, ab, ac, ad,
   // etc...
    ]) => 
      return Object.create(
        timestamp: a,
        email: b,
        response_1: c,
        response_2: d,
        response_3: e,
      );
    
  );

/*
    Appends the string to some part of the Objects in our Array.
    Since the Objects match the table structure (hopefully) we can be
    pretty specific.
    
    I tried to mock how a Google Form might collect responses.
*/
var appended_string = original_values.map(
  (arg:  timestamp; email; response_1; response_2; response_3 ) => 
    switch (typeof arg.response_1) 
      case "string":
        return Object.assign(arg, 
          response_1: (arg.response_1 += " string"),
        );

      default:
        return arg;
    
  
);

/*
    Need to reshape the "Array of Objects" back into an "Array of Arrays".
    Pretty simple compared to the original.
*/
var values_to_set = appended_string.map(
  (arg:  timestamp; email; response_1; response_2; response_3 ) => 
    return [
      arg.timestamp,
      arg.email,
      arg.response_1,
      arg.response_2,
      arg.response_3,
    ];
  
);

/*
    Here we'll take our finalized "values_to_set Array of Arrays" and
    use it as the input for ".setValues()".

    All Google Sheets data starts and ends as an "Array of Arrays" but...
    It is significantly easier to work with as an "Array of Objects".

    Rhetorical Question: Who wants to keep track of indexes?
*/
SpreadsheetApp.getActiveSheet().getRange("A:E").setValues(values_to_set);

【讨论】:

以上是关于遍历范围,将字符串附加到每个的主要内容,如果未能解决你的问题,请参考以下文章

循环遍历数组并附加到objective-C中的字符串

遍历文件时,如何将每个文件名附加到列表中? [复制]

在JavaScript中写入文件时将每个字符串附加到新行[重复]

循环遍历节点并使用 XSLT 将值附加到字符串

php 将字符附加到Array [PHP]上的每个项目

当我在快速失败时使用 try String(contentsOf: shareLog) 将每个字符串附加到 .txt 文件时