使用带有 arrayformula 的自定义公式计算 Google 表格中两列城市之间的距离

Use a custom formula with arrayformula for distances between two columns of cities in Google Sheets with App Scripts

我在 Google Apps 脚本和 Google 表格中有一个自定义公式,可以给出两个城市之间的距离。 EXAMPLE SHEET

=MILEAGE(A2,B2)

function MILEAGE(origin,destination) {
  Utilities.sleep(0);
var directions = Maps.newDirectionFinder()
  .setRegion('US')
  .setOrigin(origin)
  .setDestination(destination)
  .getDirections();

var route = directions.routes[0].legs[0];
var distance = (route.distance.value) * 0.000621371;

return distance
}

该公式适用于两个单元格,但我需要它像数组公式一样适用于两行城市每行独立。我已经权衡了 运行 脚本的优缺点,一次使用一个数组并返回一个数组,但它在此应用程序中无效,因为我不希望每次一行 [= 时都重新计算整个范围29=].

objective 是这样工作的:

=arrayformula(if(A2:A<>"", MILEAGE(A2:A, B2:B), ""))

任何有关如何执行此操作的建议,或实现相同目标的更好方法,我们将不胜感激。

问题是,当您在自定义函数中使用传递范围时,它不会传递范围,而是传递值。

我建议您专门使用触发器 onEdit 这样当您编辑特定单元格时,它只会重新计算该行。我还做到了,如果您删除或添加多个位置,它们会更新相应的行。

脚本:

function onEdit(e) {
  var sheet = e.source.getActiveSheet();
  var range = e.range;
  var row = range.getRow();
  var column = range.getColumn();
  var lastRow = range.getLastRow();

  for(var i = 0; i <= lastRow - row; i++)
    if (row + i > 1 && (column == 1 || column == 2) && sheet.getName() == "Sheet1") {
      var origin = sheet.getRange(row + i, 1).getValue();
      var destination = sheet.getRange(row + i, 2).getValue();

      if(origin && destination)
        sheet.getRange(row + i, 3).setValue(calculateMileage(origin, destination));
      else 
        sheet.getRange(row + i, 3).clearContent();
    };
}

function calculateMileage(origin, destination) {
  var directions = Maps.newDirectionFinder()
    .setRegion('US')
    .setOrigin(origin)
    .setDestination(destination)
    .getDirections();

  var route = directions.routes[0];
  if (route) {
    var legs = route.legs[0];
    var distance = (legs.distance.value) * 0.000621371;

    return distance;
  }
  else {
    return "no route found"
  }
}

输出:

注:

  • 我发现一个单元格由于位置无效或找不到路线而 returns 出错,所以我尝试捕捉它。
  • 如果某行在任一单元格中都没有值,它将删除距离列。

为了使这个函数数组兼容,

  • 检查它是否是数组并且map它是元素

  • 如果不是数组,直接传元素

  • Cache service 也可用于避免重复调用地图。

/**
 * @param {string} origin
 * @param {string} destination
 */
function MILEAGE_(origin, destination) {
  try {
    const sCache = CacheService.getScriptCache();
    const key = `${origin}_${destination}`;
    const cached = sCache.get(key);
    if (cached) return Number(cached);
    Utilities.sleep(1);
    const directions = Maps.newDirectionFinder()
      .setRegion('US')
      .setOrigin(origin)
      .setDestination(destination)
      .getDirections();
    const route = directions.routes[0].legs[0];
    const distance = route.distance.value * 0.000621371;
    sCache.put(key, String(distance), 21600);
    return distance;
  } catch {
    return '#ERROR';
  }
}

/**
 * @param {A1:D1} arr
 * @param {A1} param1
 * @param {C1} param2
 * @customfunction
 */
const mileage = (...arr) =>
  Array.isArray(arr[0]) ? arr[0].map(e => MILEAGE_(...e)) : MILEAGE_(...arr);

用法:

=MILEAGE("origin","destination")
=MILEAGE(A1,B1)
=MILEAGE(A1:B100)