最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

html - Merge table rows using JavaScript - Stack Overflow

programmeradmin2浏览0评论

I am using a normal table, here is my requirement:

If a table row values duplicate it should get merged using javascript.For eg: table 1 row values and table2 row values are same then it should get merged and the value should be displayed only once using javascript. Here I will attach my codings.

function myFunction() {
  const firstRows = { };
  let shade = false;
  
  const colsToMerge = [0, 1];

  Array.from(document.querySelectorAll('tbody tr')).forEach(tr => {
    const text = tr.children[0].innerText;
    if (!(text in firstRows)) {
      firstRows[text] = { shade, elem: tr, count: 1 };
      shade = !shade;
    } else {
      const firstRow = firstRows[text]
      firstRow.count++;
      colsToMerge.forEach(i => tr.children[i].remove());
      colsToMerge.forEach(i =>
        firstRow.elem.children[i]
                .setAttribute('rowspan', firstRow.count)
      );
    }
    if (firstRows[text].shade) tr.classList.add('dark');
  });
}
table {
  font-family: arial, sans-serif;
  border-collapse: collapse;
  width: 100%;
}

th {
  background: #a9a9a9;
}

td,
th {
  border: 1px solid #dddddd;
  text-align: center;
  padding: 8px;
  font-family: monospace;
  font-size: 17px;
}

.dark {
  background-color: #dddddd;
}
<body onload="myFunction()">
  <table>
    <tr>
      <th>Company</th>
      <th>Contact</th>
      <th>Country</th>
    </tr>
    <tbody>
      <tr>
        <td>Alfreds Futterkiste</td>
        <td>Maria Anders</td>
        <td>Germanyindgo</td>
      </tr>
      <tr>
        <td>Alfreds Futterkiste</td>
        <td>Maria Anders</td>
        <td>Germany</td>
      </tr>
      <tr>
        <td>Centro ercial Moctezuma</td>
        <td>Francisco Chang</td>
        <td>Mexico</td>
      </tr>
      <tr>
        <td>Ernst Handel</td>
        <td>Roland Mendel</td>
        <td>Austria</td>
      </tr>
      <tr>
        <td>Island Trading</td>
        <td>Helen Bennett</td>
        <td>UK</td>
      </tr>
      <tr>
        <td>Laughing Bacchus Winecellars</td>
        <td>Yoshi Tannamuri</td>
        <td>Canada</td>
      </tr>
      <tr>
        <td>Magazzini Alimentari Riuniti</td>
        <td>Giovanni Rovelli</td>
        <td>Italy</td>
      </tr>
    </tbody>
  </table>
</body>

I am using a normal table, here is my requirement:

If a table row values duplicate it should get merged using javascript.For eg: table 1 row values and table2 row values are same then it should get merged and the value should be displayed only once using javascript. Here I will attach my codings.

function myFunction() {
  const firstRows = { };
  let shade = false;
  
  const colsToMerge = [0, 1];

  Array.from(document.querySelectorAll('tbody tr')).forEach(tr => {
    const text = tr.children[0].innerText;
    if (!(text in firstRows)) {
      firstRows[text] = { shade, elem: tr, count: 1 };
      shade = !shade;
    } else {
      const firstRow = firstRows[text]
      firstRow.count++;
      colsToMerge.forEach(i => tr.children[i].remove());
      colsToMerge.forEach(i =>
        firstRow.elem.children[i]
                .setAttribute('rowspan', firstRow.count)
      );
    }
    if (firstRows[text].shade) tr.classList.add('dark');
  });
}
table {
  font-family: arial, sans-serif;
  border-collapse: collapse;
  width: 100%;
}

th {
  background: #a9a9a9;
}

td,
th {
  border: 1px solid #dddddd;
  text-align: center;
  padding: 8px;
  font-family: monospace;
  font-size: 17px;
}

.dark {
  background-color: #dddddd;
}
<body onload="myFunction()">
  <table>
    <tr>
      <th>Company</th>
      <th>Contact</th>
      <th>Country</th>
    </tr>
    <tbody>
      <tr>
        <td>Alfreds Futterkiste</td>
        <td>Maria Anders</td>
        <td>Germanyindgo</td>
      </tr>
      <tr>
        <td>Alfreds Futterkiste</td>
        <td>Maria Anders</td>
        <td>Germany</td>
      </tr>
      <tr>
        <td>Centro ercial Moctezuma</td>
        <td>Francisco Chang</td>
        <td>Mexico</td>
      </tr>
      <tr>
        <td>Ernst Handel</td>
        <td>Roland Mendel</td>
        <td>Austria</td>
      </tr>
      <tr>
        <td>Island Trading</td>
        <td>Helen Bennett</td>
        <td>UK</td>
      </tr>
      <tr>
        <td>Laughing Bacchus Winecellars</td>
        <td>Yoshi Tannamuri</td>
        <td>Canada</td>
      </tr>
      <tr>
        <td>Magazzini Alimentari Riuniti</td>
        <td>Giovanni Rovelli</td>
        <td>Italy</td>
      </tr>
    </tbody>
  </table>
</body>

Check this table

Share Improve this question edited Feb 8, 2019 at 11:13 adiga 35.3k9 gold badges65 silver badges87 bronze badges asked Feb 8, 2019 at 11:11 Thirshal Thirshal 771 silver badge7 bronze badges 3
  • What is the issue here? It looks like this duplicate Alfreds is already removed? – adiga Commented Feb 8, 2019 at 11:16
  • You can use add attribute "rowspan" in your cells to merge similiar rows. Just like that: jsfiddle/two8kq1n – Marcelo Macedo Commented Feb 8, 2019 at 11:16
  • @adiga;@Marcelo Macedo I want to set the rowspan dynamically. For eg: Let's take the first two rows if the first column is the same it should get merged dynamically instead of giving it as static. – Thirshal Commented Feb 8, 2019 at 11:20
Add a ment  | 

3 Answers 3

Reset to default 3

Here is a working solution for dynamically merging consecutive row cells in every column.

The solution also works for toggling the shade and will only toggle the shade when a 'full' row starts, which is, when there is no merging between consecutive rows.

Also, you have to wrap the tr in the table header in a thead as that can cause problems with document.querySelectorAll('tbody tr') event tbody is specified in the query.

I slightly modified the table to show the shading and merging:

function myFunction() {
  const previousRow = {};
  const colsChanged = {};
  let dark = false;

  Array.from(document.querySelectorAll('tbody tr')).forEach((tr, rowIdx) => {
    Array.from(tr.children).forEach((td, colIdx) => {
      if (rowIdx > 0 && previousRow[colIdx].text === td.innerText) {
        previousRow[colIdx].elem.setAttribute('rowspan', ++previousRow[colIdx].span);
        colsChanged[colIdx] = false;
        td.remove();
      } else {
        previousRow[colIdx] = { span: 1, text: td.innerText, elem: td, dark };
        colsChanged[colIdx] = true;
      }
    });
    const rowChanged = Object.values(colsChanged).every(Boolean);
    dark = rowChanged && rowIdx > 0 ? !dark : dark;
    if (dark) {
      tr.classList.add('dark');
    }
  });
}
table {
  font-family: arial, sans-serif;
  border-collapse: collapse;
  width: 100%;
}

th {
  background: #a9a9a9;
}

td,
th {
  border: 1px solid black;
  text-align: center;
  padding: 8px;
  font-family: monospace;
  font-size: 17px;
}

.dark {
  background-color: #dddddd;
}
<body onload="myFunction()">
  <table>
    <thead>
      <tr>
        <th>Company</th>
        <th>Contact</th>
        <th>Country</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Alfreds Futterkiste</td>
        <td>Maria Anders</td>
        <td>Germanyindgo</td>
      </tr>
      <tr>
        <td>Alfreds Futterkiste</td>
        <td>Maria Anders</td>
        <td>Germany</td>
      </tr>
      <tr>
        <td>Centro ercial Moctezuma</td>
        <td>Francisco Chang</td>
        <td>Mexico</td>
      </tr>
      <tr>
        <td>Ernst Handel</td>
        <td>Francisco Chang</td>
        <td>Austria</td>
      </tr>
      <tr>
        <td>Ernst Handel</td>
        <td>Helen Bennett</td>
        <td>UK</td>
      </tr>
      <tr>
        <td>Laughing Bacchus Winecellars</td>
        <td>Yoshi Tannamuri</td>
        <td>Canada</td>
      </tr>
      <tr>
        <td>Magazzini Alimentari Riuniti 1</td>
        <td>Giovanni Rovelli 1</td>
        <td>Italy</td>
      </tr>
      <tr>
        <td>Magazzini Alimentari Riuniti 2</td>
        <td>Giovanni Rovelli 2</td>
        <td>Italy</td>
      </tr>
      <tr>
        <td>Magazzini Alimentari Riuniti 3</td>
        <td>Giovanni Rovelli 3</td>
        <td>Italy</td>
      </tr>
    </tbody>
  </table>
</body>

According to the ments below, if you want to merge cells to the right only if the corresponding cells were merged in the first column, here is a modification to do it.

a leftMerged variable is used to keep track of whether the first cell on the current line was merged to the previous one, and if so, this enables us to merge the following cells to the right if necessary. The variable is reset to false at the beginning of each line.

function myFunction() {
  const previousRow = {};
  const colsChanged = {};
  let leftMerged = false;
  let dark = false;

  Array.from(document.querySelectorAll('tbody tr')).forEach((tr, rowIdx) => {
    Array.from(tr.children).forEach((td, colIdx) => {
      if (rowIdx > 0 && (colIdx === 0 || leftMerged) && previousRow[colIdx].text === td.innerText) {
        previousRow[colIdx].elem.setAttribute('rowspan', ++previousRow[colIdx].span);
        colsChanged[colIdx] = false;
        td.remove();
        if (colIdx === 0) {
          leftMerged = true;
        }
      } else {
        previousRow[colIdx] = { span: 1, text: td.innerText, elem: td, dark };
        colsChanged[colIdx] = true;
      }
    });
    const rowChanged = Object.values(colsChanged).every(Boolean);
    dark = rowChanged && rowIdx > 0 ? !dark : dark;
    if (dark) {
      tr.classList.add('dark');
    }
    leftMerged = false;
  });
}
table {
  font-family: arial, sans-serif;
  border-collapse: collapse;
  width: 100%;
}

th {
  background: #a9a9a9;
}

td,
th {
  border: 1px solid black;
  text-align: center;
  padding: 8px;
  font-family: monospace;
  font-size: 17px;
}

.dark {
  background-color: #dddddd;
}
<body onload="myFunction()">
  <table>
    <thead>
      <tr>
        <th>Company</th>
        <th>Contact</th>
        <th>Country</th>
        <th>Col4</th>
        <th>Col5</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Alfreds Futterkiste</td>
        <td>Maria Anders</td>
        <td>Germanyindgo</td>
        <td>1</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Alfreds Futterkiste</td>
        <td>Maria Anders</td>
        <td>Germany</td>
        <td>1</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Centro ercial Moctezuma</td>
        <td>Francisco Chang</td>
        <td>Mexico</td>
        <td>1</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Ernst Handel</td>
        <td>Francisco Chang</td>
        <td>Austria</td>
        <td>1</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Ernst Handel</td>
        <td>Helen Bennett</td>
        <td>UK</td>
        <td>1</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Laughing Bacchus Winecellars</td>
        <td>Yoshi Tannamuri</td>
        <td>Canada</td>
        <td>1</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Magazzini Alimentari Riuniti 1</td>
        <td>Giovanni Rovelli 1</td>
        <td>Italy</td>
        <td>1</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Magazzini Alimentari Riuniti 1</td>
        <td>Giovanni Rovelli 2</td>
        <td>Italy</td>
        <td>1</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Magazzini Alimentari Riuniti 3</td>
        <td>Giovanni Rovelli 3</td>
        <td>Italy</td>
        <td>1</td>
        <td>2</td>
      </tr>
    </tbody>
  </table>
</body>

In below snippet first I read data from table to js array, then merge rows with similar first column, and generate new html content of table body

let readTable = (tbody) => [...tbody.rows].map(row=>[...row.cells].map(c=>c.innerText));    

function mergeRows(data) {
  let h={};
  data.forEach((r,i)=> { // merge by first column
      let p=h[r[0]];
      if(p) { p[1].push(r[1]); p[2].push(r[2]) };
      if(!p) { h[r[0]]=[r[0],[r[1]],[r[2]],i]}         
  });     
  let result=Object.values(h).sort((a,b)=>a[3]-b[3]);  
  result.forEach(r=> {r[1]=[... new Set(r[1])]; r[2]=[... new Set(r[2])]}); // remove duplicates in seconond and third column
  return result;
}

function genTable(rows, tbody) {    
  let b="",x="";
  rows.forEach(r=> {
    x=r.map((c,i) => `<td>${c}</td>`).slice(0,3);
    b+=`<tr>${x.join('')}</tr>`;
  })
  tbody.innerHTML=b;
  console.log({b,rows, tbody});
}

function myFunction() {
  let table=document.querySelector('table');
  let tbody=table.tBodies[table.tBodies.length-1]; 
  genTable( mergeRows(readTable(tbody)), tbody );
}
table {
  font-family: arial, sans-serif;
  border-collapse: collapse;
  width: 100%;
}

th {
  background: #a9a9a9;
}

td,
th {
  border: 1px solid #dddddd;
  text-align: center;
  padding: 8px;
  font-family: monospace;
  font-size: 17px;
}

.dark {
  background-color: #dddddd;
}
<body onload="myFunction()">
  <table>
    <tr>
      <th>Company</th>
      <th>Contact</th>
      <th>Country</th>
    </tr>
    <tbody>
      <tr>
        <td>Alfreds Futterkiste</td>
        <td>Maria Anders</td>
        <td>Germanyindgo</td>
      </tr>
      <tr>
        <td>Alfreds Futterkiste</td>
        <td>Maria Anders</td>
        <td>Germany</td>
      </tr>
      <tr>
        <td>Centro ercial Moctezuma</td>
        <td>Francisco Chang</td>
        <td>Mexico</td>
      </tr>
      <tr>
        <td>Ernst Handel</td>
        <td>Roland Mendel</td>
        <td>Austria</td>
      </tr>
      <tr>
        <td>Island Trading</td>
        <td>Helen Bennett</td>
        <td>UK</td>
      </tr>
      <tr>
        <td>Laughing Bacchus Winecellars</td>
        <td>Yoshi Tannamuri</td>
        <td>Canada</td>
      </tr>
      <tr>
        <td>Magazzini Alimentari Riuniti</td>
        <td>Giovanni Rovelli</td>
        <td>Italy</td>
      </tr>
    </tbody>
  </table>
</body>

you can do this dynamically by Javascript. Check this fiddle:

let previousObj = [];
const rows = $("table tr");
rows.each(function(i, el) {
    let obj = [];
    $(el).children("td").each(function(ic, elc) {
    obj.push(elc);

    if (previousObj.length > ic) {
        if (previousObj[ic].innerHTML == obj[ic].innerHTML) {
        $(previousObj[ic]).attr('rowspan', getRowsSpan(ic, i, obj[ic].innerHTML));
        $(obj[ic]).remove();
      }      
    }
  });

  previousObj = obj;
});

function getRowsSpan(col, row, value) {
    var rowSpan = 2;
  var actualRow = row+1;

  while ($(rows[actualRow]).children("td")[col].innerHTML == value) {
    rowSpan++;
    actualRow++;
  } 

  return rowSpan;
}

https://jsfiddle/9e1bcprx/2/

发布评论

评论列表(0)

  1. 暂无评论