【问题标题】:How to update cell in Google App Script after it has been filtered过滤后如何更新Google App Script中的单元格
【发布时间】:2022-01-23 18:50:14
【问题描述】:

我有以下脚本,它们从示例电子表格的“事务”选项卡中获取数据,然后在 sumActual 中运行逻辑,例如在 sumActual 选项卡中进行分组和设置值。这一切都很好。

然而,在函数 sumActual 的末尾,我注意到 // 设置了已处理的事务。我想在“T”列中设置今天的日期以标记已处理的行。但是我需要使用 sumActuals 中的“ const filterProcessedTransactions ”,因为它会过滤掉那些已经处理过的行。我不太确定该怎么做。

任何帮助都会很棒。

样本表

https://docs.google.com/spreadsheets/d/17SId7mIzO3hVOC36Nq40O0bjPS5YfGOX4wsMU1NlbCU/edit?usp=sharing

function sumActual () {
  const _ = LodashGS.load()
  eval(UrlFetchApp.fetch('https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment-with-locales.min.js').getContentText())

  const transactions = getTransactions()
  // console.log({ transactions })

  const filterProcessedTransactions = _.filter(transactions, (o) => {
    return !moment(o.Processed).isValid()
  })
  // console.log({ filterProcessedTransactions })

  // Group By Category
  const grouped = _.groupBy(filterProcessedTransactions, (o) => {
    return [o.Category, o.Year, o.Month]
  })
  // console.log({ grouped })

  // sum amount
  const result = _.map(grouped, function (value, key) {

    const d = value[0].Month + '/1/' + value[0].Year
    const startOf = moment(d).format('MM/DD/YYYY')
    const valid = moment(startOf).isValid()
    // console.log({ d, startOf, valid })

    return {
      category: value[0].Category,
      month: value[0].Month,
      year: value[0].Year,
      amount: _.reduce(value, function (total, o) {
        return Math.abs(total + o.Amount)
      }, 0),
      startofMonth: startOf
    }
  })
  // console.log({ result })

  // lookup, merge, sum and update sumActuals
  const obj = result.reduce((o, { category, amount, startofMonth }) => (o[startofMonth + category] = amount, o), {})
  console.log({ obj })

  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('sumActual')
  const [[, ...header], , ...values] = sheet.getDataRange().getValues()
  const res = values.map(([h, ...v]) => v.map((f, j) => {
    const key = Utilities.formatDate(header[j], Session.getScriptTimeZone(), 'MM/dd/yyyy') + h
    return obj[key] ? obj[key] + f : f
  }))
  console.log({ res })

  sheet.getRange(3, 2, res.length, res[0].length).setValues(res)

  // set transactions processed
  
}

function getTransactions () {
  const ss = SpreadsheetApp.getActiveSpreadsheet()
  const transactionSheet = ss.getSheetByName('Transactions')
  const lastRow = transactionSheet.getLastRow()
  const lastColumn = transactionSheet.getLastColumn()
  const values = transactionSheet.getRange(1, 1, lastRow, lastColumn).getValues()
  const [headers, ...originalData] = values.map(([, b,, d, e,,,,,,,,,,, p, q, r, s, t]) => [b, d, e, p, q, r, s, t])
  const res = originalData.map(r => headers.reduce((o, h, j) => Object.assign(o, { [h]: r[j] }), {}))
  return res
}

【问题讨论】:

  • 当您需要索引和起始行之间的连接以获得下游新行时,这就是使用过滤器功能的问题。在那种情况下,我可能会避免使用 .filter 方法,以便我可以保持 startrow 索引和当前行之间的连接。
  • 你会怎么做?

标签: google-apps-script google-sheets lodash


【解决方案1】:

示例解决方案:

如果您愿意将脚本运行的所有事务视为已处理,最简单的方法是直接设置列值:

这个函数应该这样做:

function setTransactionsProcessed () {
  const ss = SpreadsheetApp.getActiveSpreadsheet()
  const transactionSheet = ss.getSheetByName('Transactions')
  const lastRow = transactionSheet.getLastRow()
  const lastColumn = transactionSheet.getLastColumn()
  const range = transactionSheet.getRange(2,lastColumn,lastRow-1)
  const processedArr = range.getValues();
  for (i = 0; i < processedArr.length; i++) {
    if (processedArr[i] == '') {
      const d = new(Date);
      processedArr[i][0] = d;
    }
  }
  range.setValues(processedArr);
}

这将在过滤掉已处理的事务后调用:


  const transactions = getTransactions()
   console.log({ transactions })

  const filterProcessedTransactions = _.filter(transactions, (o) => {
    return !moment(o.Processed).isValid()
  })
   console.log({ filterProcessedTransactions })

  setTransactionsProcessed();

  // Group By Category
  const grouped = _.groupBy(filterProcessedTransactions, (o) => {
    return [o.Category, o.Year, o.Month]
  })
  // console.log({ grouped })

样本输出:

【讨论】:

  • 好奇交易越多,性能问题会不会越多?假设运行 10,15,20 千行?
  • 应该有最小的性能问题,因为只有两个调用,getValues 和 setValues 而不管工作表的大小。
  • 谢谢我测试了它,它就像你提到的那样超级快。然而,在上面的示例中,一个问题是我手动设置了第 2 行到第 10 行的日期,然后记录了内部 if (processedArr[i] ... 以可视化它正在运行的内容。为什么第 11 行的 i 等于 9?由于标题行,我认为可能是 10,但对 9 感到惊讶。只是试着理解,感谢您的示例和反馈。
  • 因为 JavaScript 索引从 0 开始。第 2 行 = processesArr[0],依此类推。
【解决方案2】:

保留原始索引

您可以这样做并通过添加一个名为 idx 的额外列来保留原始索引

function getTransactions () {
  const ss = SpreadsheetApp.getActive()
  const transactionSheet = ss.getSheetByName('Sheet0')
  const lastRow = transactionSheet.getLastRow()
  const lastColumn = transactionSheet.getLastColumn()
  const values = transactionSheet.getRange(1, 1, lastRow, lastColumn).getValues()
  const [headers, ...originalData] = values.map(([, b,, d, e,,,,,,,,,,, p, q, r, s, t]) => [b, d, e, p, q, r, s, t])
  const res = originalData.map((r,i) => headers.reduce((o, h, j) => Object.assign(o, { [h]: r[j],idx:i }), {}))
  return res;
}

【讨论】:

  • 我不知道 lodash 部分,因为我从不使用它。
  • 如果性能因为数据长度而成为问题,那么我可能会在 getTransactions 中删除 map 和 reduce 并在稍后完成的过滤功能期间将 i 推到每个的末尾。这样可以保留 i 以重新计算行。
  • 所以基本上通过在获取交易时设置idx,即使在过滤后我发现使用原始索引只设置那些需要更新的交易行。这样的想法对吗?我想我的后续行动是如何使用该索引选择行和列来设置日期值?
  • row = idx + start row 和 start row 在这种情况下是 2 我相信。如果您等待在过滤器中执行此操作并仅在 getTransactions() 中获取值,那么找出列将很容易,我通常会采取简单的方法,特别是如果它可以提高性能并且我认为不在 getTransaction 中进行额外处理可能会提高性能。
  • 感谢您的解释。我最终选择了 Carlos 示例,因为它非常简单,而且正如您提到的那样简单快捷。将来我可能会使用您的 idx 示例。非常感谢您抽出宝贵时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-20
  • 1970-01-01
  • 2022-11-09
  • 2020-08-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多