将单元格引用的数字部分替换为google工作表中的另一个数字

时间:2018-03-01 14:05:28

标签: google-apps-script

我有一个脚本,可以从多个单独的工作表和中央主工作表中收集和更新数据。 单个工作表可能包含30行,而主工作表将包含所有工作表中的所有行。

单张纸可能包含公式,例如= A2 + B2。在单个工作表上,此公式位于第2行,但在主工作表上,公式可能位于第400行。因此,公式工作并在主工作表中生成正确的数据,公式需要更改为= A400 + B400。 / p>

我尝试通过查找字符串中的单元格引用来完成此工作,该字符串在开头由字母字符分隔,后跟一个或多个数字以及末尾的空格或符号,迭代通过并将与该条件匹配的每一系列数字字符替换为新的数字值。成功有限。

以下是我到目前为止的尝试。

function isNumeric(n) 
{
  return !isNaN(parseFloat(n)) && isFinite(n);
}

function isAlpha(character)
{
  if ((character.charCodeAt(0) >= 65 && character.charCodeAt(0) <= 90) ||(character.charCodeAt(0)>=97 && character.charCodeAt(0)<=122))
  {
    return true;
  }
  else
  {
    return false;
  }
}

function correctFormula(formulaValue, destinationRow)
{
  //parse through the string and identify cell references eg A2, AI77. (One or more consecutive letters followed by one or more consecutive numbers.
  //Letters will never change but numbers will corresspond to the row in the array.
  //ignore any text enclosed by ""
  //ignore any text that does not start with =

  var strFormulaValue = String(formulaValue);
  if(strFormulaValue.charAt(0) === "=")
  {
    var i = 1;
    var inQuotes = false;
    var cellRef="";
    var originalLength = strFormulaValue.length;
    while (i<originalLength)
    {
      if (strFormulaValue.charAt(i)==="\"" || strFormulaValue.charAt(i)==="\'")
      {
        if (inQuotes)
        {
          inQuotes = false;
        }
        else
        {
          inQuotes = true;
        }
      }
      if (strFormulaValue.charAt(i)==="!")
      {
        //this is a cell ref to another sheet. Get to the end of this ref
        while((isAlpha(strFormulaValue.charAt(i)))||isNumeric(strFormulaValue.charAt(i)))
        {
          i++;
        }
      }

      if (isNumeric(strFormulaValue.charAt(i)) &&(!inQuotes))
      {
        if(isAlpha(strFormulaValue.charAt(i - 1)))
        {
          //this looks like it could be the start of a cell ref
          var numStart = i;
          var numEnd = -1;
          //find the last numerical digit in this sequence
          while (isNumeric(strFormulaValue.charAt(i)))
          {
            if (isNumeric(strFormulaValue.charAt(i)))
            {
              numEnd = i;
            }
            i++
          }
          if ((!isAlpha(strFormulaValue.charAt(i)))||(i>=strFormulaValue.length))//if the sequence of numeric digits ends as a  or the number digit was the last char in the string
          {
            //replace this string of numeric digits with the desired digits
            var formulaStart = strFormulaValue.substring(0, numStart);
            var formulaEnd = strFormulaValue.substring(numEnd + 1, strFormulaValue.length);
            strFormulaValue = formulaStart+ String(destinationRow)+formulaEnd;
          }

        }
      }

      i++;
    }
  return strFormulaValue;
  }
}

function testStrToFormula(stringval)
{
  Logger.log ("Inputting =A12, should convert to A1000");
  Logger.log (correctFormula("=A12", 1000));

  Logger.log ("Inputting =A12 + B12, should convert to A1000 + B1000");
  Logger.log (correctFormula("=A12 + B12 ", 1000));

  Logger.log ("Inputting =sum(A12:D12), should convert to =sum(A1000:D1000)");
  Logger.log (correctFormula("=sum(A12:D12)", 1000));

  Logger.log("Inputting = A12 & \"D3\"");
  Logger.log(correctFormula("=A12 & \"D3\""));

  Logger.log("Inputting =Sheet1!A1 * B1")
  Logger.log (correctFormula("=Sheet1!A1 * B1"))

}

记录

[18-03-01 17:36:34:853 GMT] Inputting =A12, should convert to A1000
[18-03-01 17:36:34:854 GMT] =A1000
[18-03-01 17:36:34:854 GMT] Inputting =A12 + B12, should convert to A1000 + B1000
[18-03-01 17:36:34:855 GMT] =A1000 + B1000 
[18-03-01 17:36:34:856 GMT] Inputting =sum(A12:D12), should convert to =sum(A1000:D1000)
[18-03-01 17:36:34:858 GMT] =sum(A1000:D1000)
[18-03-01 17:36:34:859 GMT] Inputting = A12 & "D3"
[18-03-01 17:36:34:860 GMT] =Aundefined & "D3"
[18-03-01 17:36:34:861 GMT] Inputting =Sheet1!A1 * B1
[18-03-01 17:36:34:862 GMT] =Sheetundefined!A1 * B1

当我在处理引号中的值或对其他工作表中的单元格的引用时,不确定为什么我在字符串的中间未定义。 (不应改变对其他纸张上的单元格的引用)

2 个答案:

答案 0 :(得分:0)

我认为最好的解决方案是让google工作表为您进行转换。您可以使用rng.copyTo(destinationRange)将公式和值复制到主工作表。这将保留值并更新公式引用。 (相当于手动复制和粘贴行)

以下是一个示例代码:

function copyFormula() {
  var ss = SpreadsheetApp.getActive()
  var sheet = ss.getSheetByName("TargetSheet")
  var targetRng = sheet.getRange(1,1,1,3)
  Logger.log(targetRng.getValues())
  var desSheet = ss.getSheetByName("DestinationSheet")
  var desRng = desSheet.getRange(desSheet.getLastRow()+1,1,1,3)
  targetRng.copyTo(desRng)
}

此代码会将TargetSheet的第1行(第1,2,3列)中的值和公式复制到DestinationSheet的第一个可用行。它将保留硬编码值并自动更新公式。

答案 1 :(得分:0)

此代码有效,函数correctFormula(formula,destinationrow)将更新公式中的所有单元格引用,以匹配除绝对引用和外部引用之外的目标行。

function isNumeric(n) 
{
  return !isNaN(parseFloat(n)) && isFinite(n);
}

function isAlpha(character)
{
  if ((character.charCodeAt(0) >= 65 && character.charCodeAt(0) <= 90) ||(character.charCodeAt(0)>=97 && character.charCodeAt(0)<=122))
  {
    return true;
  }
  else
  {
    return false;
  }
}

function isFormulaDelimeter(character)
{
  //=, +, -, /, *, :, %, [space], (,),", ',^,<,>,&,:
  switch(character)
  {
    case "=":
      return true;
      break;
    case "+":
      return true;
      break;
    case "-":
      return true;
      break;
    case "/":
      return true;
      break;
    case "*":
      return true;
      break;
    case ":":
      return true;
      break;
    case "%":
      return true;
      break;
    case " ":
      return true;
      break;
    case "(":
      return true;
      break;
    case ")":
      return true;
      break;
      //=, +, -, /, *, :, %, [space], (,),", ',^,<,>,&,:
    case "\"":
      return true;
      break;
    case "\'":
      return true;
      break;
    case "^":
      return true;
      break;
    case "<":
      return true;
      break;
    case ">":
      return true;
      break;
    case "&":
      return true
      break;
    case ":":
      return true
      break;      
    default:
      return false;
  }
}

function getTokens(formula) {
  var strFormula = String(formula);
  var index = 0;
  var tokens = [];
  while (index < strFormula.length)
  {
    var token = "";
    while ((!isFormulaDelimeter(strFormula.charAt(index))) && (index < strFormula.length))
    {
      token = token + strFormula.charAt(index)
      index ++;
    }
    tokens.push(token)
    if (isFormulaDelimeter(strFormula.charAt(index)))
    {
      tokens.push(strFormula.charAt(index))
      index++;
    }

  }
  return(tokens);
}

function correctFormula(formulaValue, destinationRow)
{
  //Logger.log(getTokens(formulaValue));
  var tokens = getTokens(formulaValue);
  var inQuotes = false;
  for (var index = 0; index < tokens.length; index ++)
  {


    if ((String(tokens[index]).indexOf("\"")) !== -1 || (String(tokens[index]).indexOf("\'") !== -1) && (!inQuotes))
    {
      inQuotes = true;
    }
    else if(String(tokens[index]).indexOf("\"") !== -1 || (String(tokens[index]).indexOf("\'") !== -1) && (inQuotes))
    {
      inQuotes = false;
    }
    //if it's in quotes, dont touch it
    if (!inQuotes)
    {
      //is it a cell reference?
      //if it's an external cell reference dont touch it (contains !)
      //if the number is preceded by a $ symbol, dont touch it
      //TODO - absolute cell erf where letter part is absolute but not the number part.
      var token = String(tokens[index]);
      for (var n=0; n<token.length; n++)
      {
        //the cell references we are interested in are purely characters followed by numbers

        if (isAlpha(token.charAt(0))||((String(token.charAt(0))==="$") && (isAlpha(token.charAt(1)))))//if its a cell ref or the first part is absolute
        {
          var itemRef = 1;
          while(isAlpha(token.charAt(itemRef)))
          {
            itemRef++;
          }
          if (isNumeric(token.charAt(itemRef)))
          {
            var numStart = itemRef;
            while(isNumeric(token.charAt(itemRef)))
            {
              itemRef ++;
            }
            if (itemRef == token.length)//if we are at the end and it was characters followed by numbers
            {
              var charPart = token.substring(0,numStart);
              token = charPart + String(destinationRow);
              tokens[index] = token;
            }

          }

        }
      }

    }


  }
  //put it all back together and return the result
  var formula = "";
  for (n=0; n< tokens.length; n++)
  {
    formula = formula + String(tokens[n]);
  }
  return formula;
}