【问题标题】:Optimised EmEditor Macro to Element Sort, Split, and Dedupe a single Column and Extract Count优化 EmEditor 宏以对单个列和提取计数进行元素排序、拆分和重复数据删除
【发布时间】:2020-09-15 16:20:39
【问题描述】:

我目前有一个这种格式的分隔文件(3 列制表符“\t”分隔)和“;”分隔列中的所有元素)。

    COL1\tCOL2\tCOL3
    abc\t123;1q\tapple\t
    dfg\t234;2w\tapple;apple\t
    hij\t345;3e\tbanana;apple;cherry;\t
    klm\t456;4r\tapple;banana;cherry;banana;cherry;\t
    nop\t567;5t\t;;apple;banana;cherry;banana;;cherry;;\t

我想对优化的宏(最好是 javascript)有任何想法来操作文件以输出以下内容: 现在对第 3 列进行了排序(也删除了任何额外/不需要的分隔符)并删除了重复项。新的第 4 列是去重元素计数。

    abc\t123;1q\tapple\t1
    dfg\t234;2w\tapple\t1
    hij\t345;3e\tapple;banana;cherry\t3
    klm\t456;4r\tapple;banana;cherry\t3
    nop\t567;5t\tapple;banana;cherry\t3

我一直在尝试类似于下面的方法,但我认为这种方法可能更快。

    for( iRow = 2; iRow <= totalLines; iRow++ ) { //traverse eash row, start at 2nd row
      str = document.GetCell(iRow, 2, eeCellIncludeQuotes);
      var count = (str.match(/;/g) || []).length;
      var numOfElements = count + 1;
      document.SetCell( iRow, 3, numOfElements, eeAutoQuote );
    }

所以用户应该选择他们想要运行的列(本例中的第 3 列),宏将只在该列上运行,并将计数输出到右侧的新列。

真正的源文件将有数百万行,所以如果这可以通过 EmEditor 以任何方式进行优化,那就太好了。

【问题讨论】:

    标签: javascript performance emeditor


    【解决方案1】:

    我通过创建一个函数来计算字符串中的分号而不是使用正则表达式(第二版)来优化您的宏,并且还使用GetColumnSetColumn 方法来提高速度(第三版)。第三个版本将插入一列而不是覆盖现有的列。

    1. 原始宏(针对正确性和时间进行了修改)

      var start = new Date().getTime();
      
      var totalLines = document.GetLines();
      for( iRow = 2; iRow <= totalLines; iRow++ ) { //traverse eash row, start at 2nd row
          str = document.GetCell(iRow, 3, eeCellIncludeQuotes);
          var count = (str.match(/;/g) || []).length;
          var numOfElements = count + 1;
          document.SetCell( iRow, 4, numOfElements, eeAutoQuote );
      }
      
      var end = new Date().getTime();
      var time = end - start;
      alert( "Execution time: " + time + " ms" );
      
    2. 第二版

      function CountSemiColon( str )
      {
          var count = 0;
          for( var index = -1; ; ) {
              index = str.indexOf( ';', index + 1 );
              if( index == -1 ) {
                  break;
              }
              ++count;
          }
          return count;
      }
      
      var start = new Date().getTime();
      
      var totalLines = document.GetLines();
      for( iRow = 2; iRow <= totalLines; iRow++ ) { //traverse eash row, start at 2nd row
          var str = document.GetCell(iRow, 3, eeCellIncludeQuotes);
          document.SetCell( iRow, 4, CountSemiColon( str ) + 1, eeAutoQuote );
      }
      
      var end = new Date().getTime();
      var time = end - start;
      alert( "Execution time: " + time + " ms" );
      
    3. 第三版

      function CountSemiColon( str )
      {
          var count = 0;
          for( var index = -1; ; ) {
              index = str.indexOf( ';', index + 1 );
              if( index == -1 ) {
                  break;
              }
              ++count;
          }
          return count;
      }
      
      var start = new Date().getTime();
      
      var totalLines = document.GetLines();
      s1 = document.GetColumn( 3, "\n", eeCellIncludeQuotesAndDelimiter, 2, totalLines - 1 );
      sLines = s1.split( "\n" );
      s2 = "";
      nTotal = sLines.length;
      for( y = 0; y < nTotal; y++ ) {
          s2 += CountSemiColon( sLines[y] ) + 1 + "\n";
      }
      x = s2.length;
      if( x > 0 ) s2 = s2.substr( 0, x - 1 );
      document.InsertColumn( 4, s2, "\n", eeDontQuote, 2 );
      
      var end = new Date().getTime();
      var time = end - start;
      alert( "Execution time: " + time + " ms" );
      
    4. 第四版(空单元格返回 0)

      function CountElements( str )
      {
          if( str.length == 0 || str == '\t' ) {   // if empty string or delimiter only, return 0
              return 0;
          }
          var count = 0;
          for( var index = -1; ; ) {
              index = str.indexOf( ';', index + 1 );
              if( index == -1 ) {
                  break;
              }
              ++count;
          }
          return count + 1;   // add 1 to the Count
      }
      
      var start = new Date().getTime();
      
      var totalLines = document.GetLines();
      s1 = document.GetColumn( 3, "\n", eeCellIncludeQuotesAndDelimiter, 2, totalLines - 1 );
      sLines = s1.split( "\n" );
      s2 = "";
      nTotal = sLines.length;
      for( y = 0; y < nTotal; y++ ) {
          s2 += CountElements( sLines[y] ) + "\n";
      }
      x = s2.length;
      if( x > 0 ) s2 = s2.substr( 0, x - 1 );
      document.InsertColumn( 4, s2, "\n", eeDontQuote, 2 );
      
      var end = new Date().getTime();
      var time = end - start;
      alert( "Execution time: " + time + " ms" );
      

    测试结果:

    1. 10429 毫秒
    2. 8496 毫秒
    3. 1803 毫秒
    4. 1890 毫秒

    100 万行,52 MB CSV 文件。

    如果这速度不够快,或者出现“Out of Memory”错误,我会考虑其他方法或进一步优化,所以请告诉我。

    【讨论】:

    • 第三版要快得多。根据原始查询,重复数据删除/排序是否有比在“var start ...”之后添加以下 3 行更快的方法? document.SplitColumn("3",";",eeSplitIntoColumns,"A+",eeRemoveDuplicates | eeSortIgnorePrefix | eeSortStable,0); document.selection.Replace("\\t",";",eeFindReplaceCase | eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,eeExFindBOL | eeExFindEOL); document.selection.Replace("^;|;+$","",eeFindReplaceCase | eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,eeExFindBOL | eeExFindEOL);
    • 我想不出一种更快的方法来对单元格中的元素进行排序/去重。我想我必须在下一个主要版本中为宏添加一个新方法才能做到这一点。
    • 谢谢@Yutaka。只是一个小问题(我正在使用第三种方法)。如果我们正在计算的 Column 没有数据 - 例如abc\t123;1q\t\t 宏仍然返回 1,而它应该是 0。你能调整一下代码吗?
    • 然后你可以替换 s2 += CountSemiColon( sLines[y] ) + 1 + "\n";与 s2 +=str.length == 0 ? "0\n" : (CountSemiColon(sLines[y]) + 1 + "\n");
    • 至于您关于更快地对列中的元素进行排序/去重的方法的问题,请为此发布一个单独的问题,然后我将回答您的新问题。让我们一次只关注一个问题。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-24
    • 2022-01-11
    • 1970-01-01
    • 2012-12-06
    • 2021-03-17
    • 2017-07-27
    • 2019-08-20
    相关资源
    最近更新 更多