【问题标题】:Using For Loop for Inventory Update使用 For 循环进行库存更新
【发布时间】:2019-12-03 15:25:17
【问题描述】:

我正在尝试编写一个脚本来根据另一张纸上的零件清单更新我的库存。我希望它检查一张纸上一列的每一行,检查它是否与另一张纸上另一列中的任何单元格匹配(就像查找表一样。)如果该语句为真,我希望它减去我的零件清单上的编号来自库存中的编号。

我现在将检查零件清单上的零件编号是否与库存清单上同一行的零件编号匹配。这确实有效。如果不匹配,则让它检查下一行的该零件号是行不通的。

我认为 if/else 语句应该这样做,但我不知道如何表达它。我使用文件迭代器在文件夹中搜索,概念似乎相似,但我不确定如何让它遍历行。

可能还有一种更简单/更简洁的方法来设置它,但我对 Google Scripts 还很陌生,我是根据我以前做过的类似任务来设计它的。

var ss = SpreadsheetApp.getActive().getId()
var bomtactics = Sheets.Spreadsheets.Values.get(ss, 'BOM!A5:M2004')
var invtactics = Sheets.Spreadsheets.Values.get(ss, 'Inv Temp!A2:Q1658') 
var invpartno = Sheets.Spreadsheets.Values.get(ss,'Inv Temp!A2:A1658');
var invpartqty = Sheets.Spreadsheets.Values.get(ss,'Inv Temp!M2:M1658');

 for(var i = 0; i < bomtactics.values.length; i++){    
     var BOMquantity = bomtactics.values[i][0];
     var BOMpartnum = bomtactics.values[i][1];
     var invpartnum = invtactics.values[i][0];
     var invquantity = invtactics.values[i][12];

     var newqty = invquantity - BOMquantity
     Logger.log("New Quantity: ", newqty)

     //This works if the Bill of Materials list is an exact match in placement with the inventory list
     if(BOMpartnum == invpartnum){
       SpreadsheetApp.openById(ss).getRange('Inv Temp!M'+(i+2)).setValue(newqty).setFontColor('Red');
     SpreadsheetApp.flush();
   }
 else
 {
   // How to get it to check multiple records like a lookup table.
 }

示例电子表格链接:

https://docs.google.com/spreadsheets/d/1txMmXYMO549qqi5HhXL_bBHiCcXkgbLLCP3ML1xoQ9k/edit#gid=669780412

【问题讨论】:

  • 快速澄清问题。当你说,“如果不匹配,让它检查下一行的那个零件号是行不通的”,如果不匹配,你是否需要它检查库存列表中的每一行在同一行中找到,还是只需要检查 next 行?
  • 我希望它每隔一行检查一次,直到找到匹配项。
  • 查看示例电子表格会很有帮助。

标签: for-loop if-statement google-apps-script google-sheets


【解决方案1】:

这会快一点,因为您没有分别为每个零件号编写更新的数量。取而代之的是,您将更改保存到一个数组中,并在脚本末尾一次将其全部写入。

function noName() {
  var ss=SpreadsheetApp.getActive();
  var bomsh=ss.getSheetByName('BOM');
  var bomrg=bomsh.getRange(5,1,bomsh.getLastRow()-4,bomsh.getLastColumn())
  var bomvA=bomrg.getValues();
  var invsh=ss.getSheetByName('Inv Temp');
  var invrg=invsh.getRange(2,1,invsh.getLastRow()-1,invsh.getLastColumn());
  var invvA=invrg.getValues();
  var invAdj=invsh.getRange(2,13,invsh.getLastRow()-1,1).getValues();
  var invAdjfA=invsh.getRange(2,13,invsh.getLastRow()-1,1).getBackgrounds();
  invAdjfA.forEach(function(r){r[0]='#ffffff'});//resetting the background to white
  for(var i=0;i<bomvA.length;i++) {
    for(var j=0;j<invvA.length;j++) {
      //compare part numbers
      if(bomvA[i][1]==invvA[j][0]) {
        //if match is found update quantities
        invAdj[j][0]-=bomvA[i][0];
        invAdjfA[j][0]="#ff0000";
      }
    }
  } 
  //Load all quantity adjustments
  invsh.getRange(2,13,invsh.getLastRow()-1,1).setValues(invAdj);
  invsh.getRange(2,13,invsh.getLastRow()-1,1).setBackgrounds(invAdjfA);
}

//加载所有数量调整 invsh.getRange(2,13,invsh.getLastRow()-1,1).setValues(invAdj); invsh.getRange(2,13,invsh.getLastRow()-1,1).setBackgrounds(invAdjfA); }

如果多个人可以同时执行此操作,那么您可能需要对其使用锁定服务,以免数量混乱。

计算列高:

function getColumnHeight(col,sh,ss){
  var ss=ss || SpreadsheetApp.getActive();
  var sh=sh || ss.getActiveSheet();
  var col=col || sh.getActiveCell().getColumn();
  var rg=sh.getRange(1,col,sh.getLastRow(),1);
  var vA=rg.getValues();
  while(vA[vA.length-1][0].length==0){
    vA.splice(vA.length-1,1);
  }
  return vA.length;
}

【讨论】:

  • 我在样本表上进行了尝试,该样本表具有相同零件编号的重复实例。它没有对第二个实例重复该过程。 (即第 5 行和第 25 行)。我可能会想出一些东西来自动压缩和汇总第一张纸,但是有没有更简单的方法呢?
  • 我没有理由明白为什么它不能同时捕获两个实例。我没有打破循环。
  • 我看到了同样的行为。我正在调查。
  • 部分问题是 Inv Temp 页面上有很多不明显的东西。看起来它在第 50 行左右停止,但实际上我在第 1700 行找到了一些东西,所以我将数据粘贴更改为仅粘贴值,并且我删除了超出我关心的值的行。我会继续调试,看看能不能让函数工作。
  • 因为我为 Column 13 (M) 创建了一个列范围,我需要改变计算新数量的方法,所以之前发生的事情是我们只是看到最后的变化而不是全部的变化。现在我只是从 Inv Temp 的第 13 列中减去 BOM 的值,然后在最后用 setValues() 一次性发布它们。我还添加了将背景颜色更改为红色以进行更改。
【解决方案2】:

我希望它每隔一行检查一次,直到找到匹配项。

在这种情况下,我认为嵌套的 for 循环代替 if-else 语句会更好。

var ss = SpreadsheetApp.getActive().getId()
var bomtactics = Sheets.Spreadsheets.Values.get(ss, 'BOM!A5:M2004')
var invtactics = Sheets.Spreadsheets.Values.get(ss, 'Inv Temp!A2:Q1658') 
var invpartno = Sheets.Spreadsheets.Values.get(ss,'Inv Temp!A2:A1658');
var invpartqty = Sheets.Spreadsheets.Values.get(ss,'Inv Temp!M2:M1658');

  for(var i = 0; i < bomtactics.values.length; i++){    
    var BOMquantity = bomtactics.values[i][0];
    var BOMpartnum = bomtactics.values[i][1];

    for (var j = 0; j < invtactics.values.length; j++) {
      var invpartnum = invtactics.values[j][0];
      var invquantity = invtactics.values[j][12];

      if(BOMpartnum == invpartnum) {
        var newqty = invquantity - BOMquantity
        Logger.log("New Quantity: ", newqty)

        SpreadsheetApp.openById(ss).getRange('Inv Temp!M'+(j+2)).setValue(newqty).setFontColor('Red');
        SpreadsheetApp.flush();

        break;
      }
  }

请注意,当找到匹配项时,嵌套循环会以 break; 停止。

显然,这种方法的缺点是速度较慢且效率较低,尤其是当匹配通常在同一行时。如果这是一个小数据集,性能可能并不重要。

【讨论】:

  • 在我的完整库存清单(大约 1500 件商品)上,它似乎运行得很快。以及我们拥有的较大的零件清单之一。我确实有一个问题。我在具有相同零件编号的重复实例的样品表上进行了尝试。它没有重复第二个实例的过程。我可能会想出一些东西来自动压缩和汇总第一张纸,但是有没有更简单的方法呢?
  • @StephenSchonewolf - 如果您需要在找到 1 个匹配项后继续检查,只需删除 break; 行,内部循环将继续执行,直到遍历整个库存表。
  • 删除中断并没有做到这一点。当脚本运行时,我从 Inv Temp 选项卡中看到了它。它减去第一个实例,然后从原始值中减去第二个实例。 (所以 5-2=3,然后 5-1=4)而不是(5-2=3,然后 3-1 = 2)。我试图在休息后刷新另一个电子表格,它只是导致脚本在一个永无止境的循环中运行。
  • 我明白了。让我问几个后续问题:同一个零件在库存表上出现多次是否有原因?对于同一项目的所有实例,起始数量是否始终相同?您可以更新代码,以便首先将newqty 声明为 内部for 循环之外,然后在循环遇到零件的新实例时更新内部for 循环内部的newqty .也就是说,这是添加逻辑来调整同一零件的多行中的数量,而最好只清理库存列表以使唯一零件仅在列表中出现一次。
  • 重复项将显示在 BOM 表上,而不是 Inv Temp 表上。相同零件可能出现在 BOM 表上的原因是因为我可能有许多技术人员添加了他们使用的零件,而没有搜索该零件是否以前使用过。我可以测试移动 newqty 是否可以解决这个问题,以便我可以了解更多信息。谢谢。
【解决方案3】:

如果您想要从库存表中的所有行中搜索,您可以在要查找的范围内使用createTextFinder()。这将为您提供直接的答案,而无需遍历所有行。

function test() {

  var ss = SpreadsheetApp.getActive().getId()
  var bomtactics = Sheets.Spreadsheets.Values.get(ss, 'BOM!A5:M2004')
  var invtactics = Sheets.Spreadsheets.Values.get(ss, 'Inv Temp!A2:Q1658') 
  var invpartno = Sheets.Spreadsheets.Values.get(ss,'Inv Temp!A2:A1658');
  var invpartqty = Sheets.Spreadsheets.Values.get(ss,'Inv Temp!M2:M1658');

  var lookupRange = SpreadsheetApp.openById(ss).getRange('Inv Temp!A2:Q1658');

 for(var i = 0; i < bomtactics.values.length; i++){    
   var BOMquantity = bomtactics.values[i][0];
   var BOMpartnum = bomtactics.values[i][1];
   var invpartnum = invtactics.values[i][0];
   var invquantity = invtactics.values[i][12];

   var textFinder = lookupRange.createTextFinder(BOMpartnum);
   var foundRange = textFinder.findNext();


   var newqty = invquantity - BOMquantity
   Logger.log("New Quantity: ", newqty)


   //This works if the Bill of Materials list is an exact match in placement with the inventory list
   if(foundRange != null){
     SpreadsheetApp.openById(ss).getRange('Inv Temp!M'+(foundRange.getRow())).setValue(newqty).setFontColor('Red');
     SpreadsheetApp.flush();
   }else{
     // How to get it to check multiple records like a lookup table.

   }
 }
}

此外,如果您想匹配整个单元格值而不是部分匹配,您可以使用matchEntireCell

如果您想了解更多信息,可以查看TextFinder 文档。


我想建议的另一件事是,您实际上可以在不直接调用 API 的情况下获得相同的结果。意思是,而不是:

  var ss = SpreadsheetApp.getActive().getId()
  var bomtactics = Sheets.Spreadsheets.Values.get(ss, 'BOM!A5:M2004')
  var invtactics = Sheets.Spreadsheets.Values.get(ss, 'Inv Temp!A2:Q1658') 
  var invpartno = Sheets.Spreadsheets.Values.get(ss,'Inv Temp!A2:A1658');
  var invpartqty = Sheets.Spreadsheets.Values.get(ss,'Inv Temp!M2:M1658');

  var lookupRange = SpreadsheetApp.openById(ss).getRange('Inv Temp!A2:Q1658');

您可以尝试直接通过 APPs Script 类获取此信息,更具体地说是SpreadsheetApp

  var ss = SpreadsheetApp.getActive();
  var bomtactics = ss.getRange('BOM!A5:M2004').getValues();
  var invtactics = ss.getRange(ss, 'Inv Temp!A2:Q1658').getValues() ;
  var invpartno = ss.getRange(ss,'Inv Temp!A2:A1658').getValues();
  var invpartqty = ss.getRange(ss,'Inv Temp!M2:M1658').getValues();

  var lookupRange = ss.getRange('Inv Temp!A2:Q1658');

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多