【问题标题】:How can I delete duplicates without removing my formula other columns?如何在不删除公式其他列的情况下删除重复项?
【发布时间】:2020-03-31 01:14:15
【问题描述】:

这是我的简单表格:

Col A:       Col B:    Col C:                       Col D:
010Mar0100   Link      =mid(A2,find(".",A2)-10,10)  =vlookup(C2,MAIN!B:B,1,false)
010Mar0110   Link      =mid(A3,find(".",A3)-10,10)  =vlookup(C3,MAIN!B:B,1,false)
020Mar0100   Link      =mid(A4,find(".",A4)-10,10)  =vlookup(C4,MAIN!B:B,1,false)
020Mar0100   Link      =mid(A5,find(".",A5)-10,10)  =vlookup(C5,MAIN!B:B,1,false)
030Mar0100   Link      =mid(A6,find(".",A6)-10,10)  =vlookup(C6,MAIN!B:B,1,false)

Col C 和 D 具有我需要的返回值的公式。 Col A 和 B 每天刷新

我想添加一个脚本来删除重复项而不删除我在 col C 和 D 中的公式

每次我使用这个脚本时,它都会这样做。

function removeDuplicates() {
  var ss = SpreadsheetApp.getActive();
  var sheet = ss.getSheetByName('Data');
 var rng = sheet.getRange("A2:B")
var data = rng.getValues();
var newData = new Array();
for(i in data){
var row = data[i];
var duplicate = false;
for(j in newData){
  if(row.join() == newData[j].join()){
    duplicate = true;
  }
}
if(!duplicate){
  newData.push(row);
}
}
rng.clearContents();
sheet.getRange(1, 1, newData.length, 
newData[0].length).setValues(newData);
}

我需要进行哪些更改才能使其仅关注 Col A 和 B?

【问题讨论】:

  • 只删除重复的行。
  • 尝试使用内置 removeDuplicates 录制宏?
  • 我认为没有clearContents()的方法,在getRange(1, 1, newData.length, newData[0].length)的情况下,将值放在第一行。这种情况下,修改成clearContent()getRange(2, 1, newData.length, newData[0].length)怎么样?顺便说一句,关于change to get it to just focus on Col A and B only,我认为在您修改的脚本中,修改了“A”和“B”列,而没有修改“C”和“D”列。这种情况是你期望的结果吗?如果我误解了您的问题,我深表歉意。

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


【解决方案1】:

使用内置removeDuplicates:

const remD = () =>
  SpreadsheetApp.getActive()
    .getSheetByName('Data')
    .getRange('A2:B')
    .removeDuplicates();

【讨论】:

    【解决方案2】:

    解决方案

    您想要做的归结为从列表中过滤掉非唯一元素。如果我正确理解您想要保留列 CD 完整,您只需要通过引入辅助函数来使您的逻辑更清晰(顺便说一句,includes 需要 switching to V8 runtime 如果这不是一个新项目)和setValuesrng 相同(见下文改进 3)

    /**
     * Removes duplicates
     * @param {any[]} array 2D input
     * @returns {any[]} filtered input
     */
    const filterDuplicates = (array) => {
      const stack = [];
      
      return array.filter(elem => {
        const joined = elem.join('');
        
        if( stack.includes(joined) ) {
          return false;
        }
        
        stack.push(joined);
      
        return true;
      });
    };
    
    const [ input, output, tbody, tbody_init ] = document.querySelectorAll('#input, #output, #grid_init, #grid');
    
    const buildCellGrid = (parent, grid) => {
      parent.innerHTML = '';
    
      for(const row of grid) {
        const tr = document.createElement('tr');
        
        for(const cell of row) {
          const td = document.createElement('td');
          td.textContent = cell;
          tr.append(td);
        }
        
        parent.append(tr);
      }
    };
    
    input.addEventListener('change',event=> {
      const { value } = event.target;
      
      const parsedVal = JSON.parse(value);
      
      const filtered = filterDuplicates(parsedVal);
      
      output.textContent = JSON.stringify(filtered, null, '');
      
      buildCellGrid(tbody_init, filtered);
     
      buildCellGrid(tbody, parsedVal);
    });
    p, textarea {
      margin: 0 2vw; 
    }
    
    textarea {
      padding: 2vh 2vw;
      line-height: 2;
      resize: none;
      width: 125px;
      height: 125px;
    }
    
    table {
      margin: 2vh 2vw;
      border-collapse: collapse;
    }
    
    th, td {
      padding: 1vh;
      border: 1px solid darkgrey;
    }
    <p>
    Enter valid 2D Array
    </p>
    <textarea id="input"></textarea>
    <textarea id="output"></textarea>
    
    <table>
      <caption>Initial</caption>
      <tbody id="grid_init"></tbody>
    </table>
    
    <table>
      <caption>Filtered</caption>
      <tbody id="grid"></tbody>
    </table>

    改进

    1. 您可以从调用 Array 构造函数 (new Array()) 切换到更常见且不那么冗长的文字符号 []。它唯一有趣的功能是用new Array(N) 分配一个长度为N 的空Array(我有时将它与fill(whatever_value) 一起使用)。
    2. 不要在数组上使用for...in - 这个语句有它自己的用途(使用Objects)。请使用为工作量身定制的for...of
    3. 您不需要 getRange() new Range 获取更新的值,因为根据定义,它们是原始值的子网格,并且您使用 clearContents() 抢先清除范围 [我相信 you meant clearContent()]。

    备注

    1. 如果您有兴趣,这里是创建新 Array 实例的方法的半交互式比较:

    const li = (parent) => (txt) => {
      const elem = document.createElement('li');
      elem.textContent = txt || 'undefined';
      parent.append(elem);
    }
    
    const forAll = (...args) => (callback) => {
      for(const arg of args) {
        callback(arg);
      }
    };
    
    const genConstrArr = (numElems) => {
      const temp = [];
     
      let i = 0;
      while(i < numElems) {
        temp.push(`${i++} elem`);
      }
      
      return new Array(...temp);
    };
    
    const genArr = (numElems) => {
      const arr = [];
      
      let i = 0;
      while(i < numElems) {
        arr.push(`${i++} elem`);
      }
      
      return arr;
    };
    
    const buildList = (container, array) => {
      const lists = container.querySelectorAll('ul');
      const [inList, ofList, keysList, ownList, symList] = lists;
      
      forAll(...lists)(list => list.innerHTML = '');
      
      for(const key in array) {
        li(inList)(key);
      }
    
      forAll(...array)( li(ofList) );
    
      const emptyKeys = Object.keys(array);
      forAll(...emptyKeys)( li(keysList) );
    
      const names = Object.getOwnPropertyNames(array);
      forAll(...names)( li(ownList) );
    
      const symbols = Object.getOwnPropertySymbols(array);
      forAll(...symbols)( li(symList) );
    
      forAll(...lists)(list => !list.hasChildNodes() && li(list)('[Empty]')); 
    };
    
    const boxes = document.querySelectorAll('#constructor, #constructor_filled, #literal');
    
    const form = document.forms.array_control;
    form.addEventListener('change', event => {
      const { value } = event.target;
    
      buildList(boxes[0], new Array(+value));  
      buildList(boxes[1], genConstrArr(+value));
      buildList(boxes[2], genArr(+value));
    });
    input {
      margin: 4vh 0;
    }
    
    ul {
      list-style: none;
      padding: 0;
    }
    
    table {
      border-collapse: collapse;
    }
    
    th, td {
      border: 1px solid black;
      padding: 2vh 2vw;
    }
    
    td {
      vertical-align: baseline;
    }
    <form id="array_control">
      <label for="elems">Number of elements:</label>
      <input id="elems" min="0" name="elems" type="number" />
    </form>
    
    <table>
      <thead>
        <tr>
          <th></th>
          <th>for...in</th>
          <th>for...of</th>
          <th>Object.keys()</th>
          <th>getOwnPropertyNames()</th>
          <th>getOwnPropertySymbols()</th>
        </tr>
      </thead>
      <tbody>
      
        <tr id="constructor">
          <th>Constructor (1 arg)</th>
    
          <td><ul></ul></td>
          <td><ul></ul></td>
          <td><ul></ul></td>
          <td><ul></ul></td>
          <td><ul></ul></td>
        </tr>    
      
        <tr id="constructor_filled">
          <th>Constructor (>1 arg)</th>
    
          <td><ul></ul></td>
          <td><ul></ul></td>
          <td><ul></ul></td>
          <td><ul></ul></td>
          <td><ul></ul></td>
        </tr>
    
        <tr id="literal">
          <th>Literal</th>
    
          <td><ul></ul></td>
          <td><ul></ul></td>
          <td><ul></ul></td>
          <td><ul></ul></td>
          <td><ul></ul></td>
        </tr>
      </tbody>
    </table>

    参考文献

    1. JS Lexical grammar MDN 上
    2. Enumerability and ownership 的属性
    3. Set MDN 内置 docs
    4. for...in docsfor...of docs 在 MDN 上

    【讨论】:

      【解决方案3】:

      在 D 列之后添加一列,并在该列中添加此函数。它基本上会根据 col A 和 col B 中的值将唯一/重复文本添加到唯一或双精度行。您甚至可以在 If 函数之前使用 ArrayFormula

      =ArrayFormula(IF(COUNTIFS($A$1:$A1,A1, $B$1:$B1,B1)=1, "Unique", "Duplicate")))

      填写完此列后,您可以仅过滤列 E 上“唯一”的行,并且您的所有唯一行都将保持公式不变

      现在,老实说,我在网上获取了这些信息。我试图研究它以供参考,但找不到了。

      【讨论】:

        【解决方案4】:

        getValues() 返回一个二维数组。如果要删除重复的行,则必须删除重复的内部数组(外部数组的元素)。

        您可以使用reducesome 来做到这一点,如下所示:

        function removeDuplicates() {
          var ss = SpreadsheetApp.getActive();
          var sheet = ss.getSheetByName('Data');
          var rng = sheet.getRange("A2:B")
          var data = rng.getValues();
          var uniqueRows = data.reduce((unique, row) => {
            var duplicate = unique.some(uniqueRow => JSON.stringify(uniqueRow) === JSON.stringify(row));
            if (!duplicate) unique.push(row);
            return unique;
          }, []);
          rng.clearContent();
          sheet.getRange(2, 1, uniqueRows.length, uniqueRows[0].length).setValues(uniqueRows);
        }
        

        获得唯一行数组(称为uniqueRows)后,您只需:

        • 删除clearContent()(不是clearContents())范围内所有以前的内容。
        • 使用setValuesuniqueRows 写入您的工作表。

        注意:

        • 您必须enable V8 才能使此脚本正常工作。

        参考:

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-07-06
          • 2011-07-14
          • 1970-01-01
          • 2015-10-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多