【问题标题】:How can I make my Google Apps Script function work on cell input?如何使我的 Google Apps 脚本功能在单元格输入上工作?
【发布时间】:2021-11-24 21:09:11
【问题描述】:

我有a project that I've been working on for a bit。我在这里得到了一些很好的帮助,我想我差不多完成了,只需要一点帮助才能让它工作。

脚本查看 Google 表格并获取在 A 列中输入的地名,并使用 Google Places API 查找有关它的请求信息(地址、电话号码等)

我需要的最后一点帮助将能够实现单元格输入组件。最后一个帮助我的用户说

function writeToSheet(){
  var ss = SpreadsheetApp.getActiveSheet();
  var data = COMBINED2("Food");
  var placeCid = data[4];
  var findText = ss.createTextFinder(placeCid).findAll();
  if(findText.length == 0){
    ss.getRange(ss.getLastRow()+1,1,1, data.length).setValues([data])
  }
} 

将能够使用 TextFinder 检查工作表中是否存在位置 url。如果 TextFinder 的结果为 0,它将调用COMBINED2() 来获取地点信息并用writeToSheet() 填充Sheet

他们注意到

您可以在 COMBINED2 中使用单元格输入,方法是使用 ss.getRange(range).getValue()

没有编码背景,我已经能够自己将大部分内容拼接在一起,但我可以使用一些帮助来将这种功能添加到我的代码中。任何帮助或指导都会很棒。

这是完整的代码:

// This location basis is used to narrow the search -- e.g. if you were
// building a sheet of bars in NYC, you would want to set it to coordinates
// in NYC.
// You can get this from the url of a Google Maps search.
const LOC_BASIS_LAT_LON = "40.74516247433546, -73.98621366765816"; // e.g. "37.7644856,-122.4472203"

function COMBINED2(text) {
  var API_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxx';
  var baseUrl = 'https://maps.googleapis.com/maps/api/place/findplacefromtext/json';
  var queryUrl = baseUrl + '?input=' + text + '&inputtype=textquery&key=' + API_KEY + "&locationbias=point:" + LOC_BASIS_LAT_LON;
  var response = UrlFetchApp.fetch(queryUrl);
  var json = response.getContentText();
  var placeId = JSON.parse(json);
  var ID = placeId.candidates[0].place_id;
  var fields = 'name,formatted_address,formatted_phone_number,website,url,types,opening_hours';
  var baseUrl2 = 'https://maps.googleapis.com/maps/api/place/details/json?placeid=';
  var queryUrl2 = baseUrl2 + ID + '&fields=' + fields + '&key='+ API_KEY + "&locationbias=point:" + LOC_BASIS_LAT_LON;

  if (ID == '') {
    return 'Give me a Google Places URL...';
  }

  var response2 = UrlFetchApp.fetch(queryUrl2);
  var json2 = response2.getContentText();
  var place = JSON.parse(json2).result;

  var weekdays = '';
  place.opening_hours.weekday_text.forEach((weekdayText) => {
    weekdays += ( weekdayText + '\r\n' );
  } );

  var data = [
    place.name,
    place.formatted_address,
    place.formatted_phone_number,
    place.website,
    place.url,
    weekdays.trim()
  ];

  return data;
}

function getColumnLastRow(range){
  var ss = SpreadsheetApp.getActiveSheet();
  var inputs = ss.getRange(range).getValues();
  return inputs.filter(String).length;
}

function writeToSheet(){
  var ss = SpreadsheetApp.getActiveSheet();
  var data = COMBINED2("Food");
  var placeCid = data[4];
  var findText = ss.createTextFinder(placeCid).findAll();
  if(findText.length == 0){
    ss.getRange(ss.getLastRow()+1,1,1, data.length).setValues([data])
  }
}

function onOpen() {
  const ui = SpreadsheetApp.getUi();  
  ui.createMenu("Custom Menu")
      .addItem("Get place info","writeToSheet")
      .addToUi();
}

更新

这是一个共享工作表的链接,以防有人想和我一起工作。

https://docs.google.com/spreadsheets/d/1KGsk6nkin1CUgpjfHU_AdhF17T_Eh41_g4MLb1CG_Tk/edit#gid=2100307022

这是我可能没有正确表达的内容。

我希望能够在 A 列中输入地名

然后,我希望能够使用自定义菜单功能运行该功能。如果 TextFinder 没有找到给定地点的地点 URL,它将查找数据并将其写入工作表。

我想以此限制 API 调用的数量,并确保将数据写入工作表,这样就不需要在每次重新打开工作表时拉取数据。


成品

非常感谢 Lamblichus 与我一起坚持这一点。我希望有一天这对其他人有所帮助。

这是完成的代码:

// This location basis is used to narrow the search -- e.g. if you were
// building a sheet of bars in NYC, you would want to set it to coordinates
// in NYC.
// You can get this from the url of a Google Maps search.
const LOC_BASIS_LAT_LON = "ENTER_GPS_COORDINATES_HERE"; // e.g. "37.7644856,-122.4472203"

function COMBINED2(text) {
  var API_KEY = 'ENTER_API_KEY_HERE';
  var baseUrl = 'https://maps.googleapis.com/maps/api/place/findplacefromtext/json';
  var queryUrl = baseUrl + '?input=' + text + '&inputtype=textquery&key=' + API_KEY + "&locationbias=point:" + LOC_BASIS_LAT_LON;
  var response = UrlFetchApp.fetch(queryUrl);
  var json = response.getContentText();
  var placeId = JSON.parse(json);
  var ID = placeId.candidates[0].place_id;
  var fields = 'name,formatted_address,formatted_phone_number,website,url,types,opening_hours';
  var baseUrl2 = 'https://maps.googleapis.com/maps/api/place/details/json?placeid=';
  var queryUrl2 = baseUrl2 + ID + '&fields=' + fields + '&key='+ API_KEY + "&locationbias=point:" + LOC_BASIS_LAT_LON;

  if (ID == '') {
    return 'Give me a Google Places URL...';
  }

  var response2 = UrlFetchApp.fetch(queryUrl2);
  var json2 = response2.getContentText();
  var place = JSON.parse(json2).result;

  var weekdays = '';
  if (place.opening_hours && place.opening_hours.weekday_text) {
    place.opening_hours.weekday_text.forEach((weekdayText) => {
      weekdays += ( weekdayText + '\r\n' );
    } );
  }

  var data = [
    place.name,
    place.formatted_address,
    place.formatted_phone_number,
    place.website,
    place.url,
    weekdays.trim()
  ];

  return data;
}

function writeToSheet() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const FIRST_ROW = 2;
  const sourceData = sheet.getRange(FIRST_ROW, 1, sheet.getLastRow()-FIRST_ROW+1, 6)
                          .getValues().filter(row => String(row[0]));
  for (let i = 0; i < sourceData.length; i++) {
    const sourceRow = sourceData[i];
    if (sourceRow[4] === "") {
      const text = sourceRow[0];
      const data = COMBINED2(text);
      sheet.getRange(FIRST_ROW+i, 2, 1, data.length).setValues([data]);
    }
  }
}

function onOpen() {
  const ui = SpreadsheetApp.getUi();  
  ui.createMenu("Custom Menu")
      .addItem("Get place info","writeToSheet")
      .addToUi();
}

【问题讨论】:

  • 那么您希望COMBINED2(text) 中的参数text 引用某个单元格中的值吗?你知道那是哪张纸上的哪个单元格吗?也许它会是当前选定的单元格?
  • 是的,我希望能够在 A 列中输入一个地点的名称,并让 COMBINED2 函数引用它,并使用请求的信息填充同一行中它旁边的单元格。
  • 好的,A 列,但是如何定义行?它会是选定的单元格吗?根据您提供的信息,还不清楚。
  • 对不起,口齿不清。请参阅上面的屏幕截图和共享工作表的链接。我希望能够在 A 列的 Google 表格中输入地点名称。然后它使用自定义菜单函数调用 COMBINED2 函数来查看每个条目,如果 TextFinder 检查并发现它在地点 URL,它将运行该函数以提取该地点的数据并将其写入工作表。关键是它只有在发现没有地方 URL 来限制我的 API 调用时才运行该函数,并且它会将信息写入工作表,因此每次打开工作表时都不会查找它。

标签: javascript google-apps-script google-sheets google-places-api


【解决方案1】:

通过使用事件触发功能...

function onEdit(e){
SpreadsheetApp.getActiveSheet().getRange(insert your range in A1 format).setValue("anything you want to add into the cell")
}

function onEdit(e){
  var ss = SpreadsheetApp.getActiveSheet();
  var data = COMBINED2("Food");
  var placeCid = data[4];
  var findText = ss.createTextFinder(placeCid).findAll();
  if(findText.length == 0){
    ss.getRange(ss.getLastRow()+1,1,1, data.length).setValues([data])
  }
} 

你需要明确告诉谷歌应用程序脚本该函数是这样的,这样你的函数就会在称为 e 的事件对象发生时执行。 你可以在Simple Triggers阅读更多关于它的信息

【讨论】:

  • 我希望将其作为自定义菜单功能运行。使用触发器可能会起作用,但是当我将其扩展一点时,我希望保持它使用工作表中的一些不同功能。在下一次迭代中,我希望有一个单独的函数来检查工作表中已有的数据是否有更新,并在有变化时突出显示一个单元格,这样我就知道要更新我的 Google MyMap。
【解决方案2】:

预期目标:

如果我理解正确,对于列 A 中的每个值,您希望从 Maps API 检索一些相关数据并将其粘贴到列 B-F如果当前未填充列 E.

问题:

  • 您只提供了ACOMBINED2 列中的最后一个值,但您想遍历A 列中的所有值并获取所有这些值的所需信息(只要Place URL - 列 E- 尚未填充)。
  • 如果您想在Place URL 未填充的情况下避免调用 Maps API,则使用 TextFinder after 调用 Maps API 没有意义;如果您这样做,您不会限制对 API 的调用。如果您只想检查Place URL 列是否已填充,我建议检查单元格是否为空,如果为空则调用 Maps API。

建议的工作流程:

  • 使用@987654322从工作表中检索所有值,不仅包括A列,还包括E(出于实际目的,在下面的示例中获取了所有6列,因为它可以在一次调用中完成) @。
  • 遍历行(例如,使用for),并为每一行检查E 中的单元格是否已填充。
  • 如果E (Place URL) 中的单元格为空,请使用A 中的值作为COMBINED2 的参数,并将生成的data 写入列B-F,就像您当前所做的那样.

代码示例:

function writeToSheet() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const FIRST_ROW = 2;
  const sourceData = sheet.getRange(FIRST_ROW, 1, sheet.getLastRow()-FIRST_ROW+1, 6)
                          .getValues().filter(row => String(row[0]));
  for (let i = 0; i < sourceData.length; i++) {
    const sourceRow = sourceData[i];
    if (sourceRow[4] === "") {
      const text = sourceRow[0];
      const data = COMBINED2(text);
      sheet.getRange(FIRST_ROW+i, 2, 1, data.length).setValues([data]);
    }
  }
}

更新:

对于 Places API 不返回 opening_hours 的名称,请考虑先检查它是否存在:

function COMBINED2(text) {

  // ... REST OF YOUR FUNCTION ...

  var weekdays = '';
  if (place.opening_hours && place.opening_hours.weekday_text) {
    place.opening_hours.weekday_text.forEach((weekdayText) => {
      weekdays += ( weekdayText + '\r\n' );
    } );
  }

  var data = [
    place.name,
    place.formatted_address,
    place.formatted_phone_number,
    place.website,
    place.url,
    weekdays.trim()
  ];

  return data;
}

【讨论】:

  • 我尝试在覆盖 writeToSheet() 的内容并删除 getColumnLastRow(range) 后使用它,但它仅适用于第一个条目。之后,工作表引发了错误TypeError: Cannot read property 'weekday_text' of undefined。不过看起来很接近了!
  • 实际上,我的立场是正确的。它在一定程度上起作用。尝试在 OP 中的示例屏幕截图/共享表中的 3 个位置运行它时出现该错误。我试图在我为度假而构建的工作表中的另一组地方使用它。第一行是我的 AirBnB,所以它不会为此找到任何东西,它跳过它也没问题。它能够找到第 3-8 行的信息,但随后它因同样的错误 TypeError: Cannot read property 'weekday_text' of undefined 而停止,并且对第 9-54 行没有执行任何操作。这是 Google Places API 错误还是与 JS 有关?
  • @JeffAbrahams 这与 Places API 有关,因为来自列 A 的值被正确返回并作为参数传递给 COMBINED2(这是您的主要问题)。对于列A 中的某些值,opening_hours 不可用。作为一种解决方法,我建议在尝试从中获取信息之前检查opening_hours 是否存在,如果不存在则将单元格留空。请参阅我答案底部的Updated 部分。
  • 我是个白痴……你从一开始就成功了。我得到所有这些错误的原因是因为我忽略了用正确的城市替换LOC_BASIS_LAT_LON GPS 坐标。在搜索圣地亚哥的地方时,我使用的是 NYC 坐标。我只是在我的 50 多个圣地亚哥地点的表格上运行它,它们都完美地回来了。非常感谢您对此的帮助。我希望有一天它对您和其他人有所帮助。如果你能到纽约,我欠你一杯啤酒和一片披萨。
  • @JeffAbrahams 当然,在失败行之前var ID = placeId.candidates[0].place_id; 添加if (!placeId.candidates[0]) return new Array(6);。这样函数结束并返回一个空数组。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多