0

Newbie here how can I sort all of the following table row attach an onclick listener on the header after it is displayed.

ID Name Inventory Volume
1 Rachel Data is not enough
2 Ross 100
3 Monica 1
4 Connor Data is not enough
5 Dustin -5

into this sorting example is in descending, the words should be the last.

ID Name Inventory Volume
2 Ross 100
3 Monica 10
5 Dustin -5
1 Rachel Data is not enough
4 Connor Data is not enough

But I also want the other columns to be sorted as well and the other columns to have the function to sort as well.

Tried this solution but only works for the column. https://jsfiddle.net/7wnke5q2/

function sortData(data, method) {
  let lessData = 'Data Not Enough'
  let lastItems = []
  let sortedList;
  if (method == 'descending') {
    sortedList = data.sort((a, b) => {
      return a - b
    })
  } else if (method == 'ascending') {
    sortedList = data.sort((a, b) => {
      return b - a
    })
  }
  for (let i = 0; i < sortedList.length; i++) {
    if (sortedList[i] == lessData) {
      let item = sortedList[i]
      sortedList.splice(i, 1)
      sortedList.push(item)
    }
  }
  sortedList = sortedList.concat(lastItems)
  return sortedList
}

Could you please help me out. Thanks in advance!

Christian
  • 21
  • 5
  • First, you should not use the string "Data Not Enough" in the actual dataset, just use undefined. Maybe have a look at https://lodash.com/docs/4.17.15#sortBy – Moritz Makowski Jul 19 '21 at 17:40
  • @MoritzMakowski It should be but I can't replace the "Data Not Enough" thats what makes it complicated. I can't use third party library for this. – Christian Jul 19 '21 at 17:44
  • @MisterJojo sorry made a mistake corrected should follow the correct order. When the Inventory Volume is clicked all the other table column should follow. – Christian Jul 19 '21 at 18:06
  • @MisterJojo same logic with any other table sort but difference is the words in the column should go last and not first and also all the other rows should follow. Thanks – Christian Jul 19 '21 at 18:08
  • Does this answer your question? [Sort an array to have specific items first in the array](https://stackoverflow.com/questions/6974069/sort-an-array-to-have-specific-items-first-in-the-array) – Heretic Monkey Jul 19 '21 at 18:53
  • @HereticMonkey it does not solve the criteria which is to sort it by rows. I also want the other columns to be sorted as well and the other columns to have the function to sort as well. – Christian Jul 19 '21 at 23:57

3 Answers3

2

If i understand, you need all "Data Not Enough" at the end. So, it can works:

function sortData(data, method) {
    let lessData = 'Data Not Enough'
    let sortedList;  
    sortedList = data.sort((a, b) => {
        if(a=='Data Not Enough')
            return 1
        if(b=='Data Not Enough')
            return -1
      return method=='ascending'?a-b:b-a
      })
    return sortedList
}
YamirL
  • 65
  • 2
  • 8
  • Agreed but I was able to this. How can I sort it by row i want the other rows to follow? "I also want the other columns to be sorted as well and the other columns to have the function to sort as well" – Christian Jul 19 '21 at 18:11
  • What must be the columns priority to order? Maybe you want is: https://stackoverflow.com/questions/6913512/how-to-sort-an-array-of-objects-by-multiple-fields – YamirL Jul 19 '21 at 18:24
  • Numerical and alphabetical but for those columns that have "Data Not Enough" or mixed words and numbers in columns. The words should go at the end or last like the "Data Not Enough" – Christian Jul 19 '21 at 18:27
  • I was able to sort the column but not the row. – Christian Jul 19 '21 at 18:29
1

something like that ?
just click on header columns to sort the table

const 
  myTableHead     = document.querySelector('#my-table thead')
, myTableHead_TH  = document.querySelectorAll('#my-table thead th')
, myTableBody     = document.querySelector('#my-table tbody')
, myTableBody_TR  = [...document.querySelectorAll('#my-table tbody tr')]
, sortOrder       = ['','asc','desc']
, isDNE = str => str.trim() === 'Data is not enough'
  ;
myTableHead.onclick = ({target}) =>
  {
  if (!target.matches('th')) return
  let idx = (sortOrder.findIndex(x=>x===target.className) +1) %3
  myTableHead_TH.forEach(th=>th.className='')
  target.className = sortOrder[idx]

  if ( sortOrder[idx] )
    {
    myTableBody_TR
      .sort(dynaSort(target.cellIndex, target.dataset.type, sortOrder[idx] ))
      .forEach(tr=>myTableBody.appendChild(tr) )
    }
  }

function dynaSort( colIndex, colType, order='asc' )
  {
  let sortOrder = (order === 'desc') ? -1 : 1
 
  return function(row_a,row_b)
    {
    let a = row_a.cells[colIndex].textContent
      , b = row_b.cells[colIndex].textContent
      ;
    if (isDNE(a) && isDNE(b)) return 0
    if (isDNE(a))  return +1
    if (isDNE(b)) return -1

    if (colType==='str')  return (a.trim().localeCompare(b.trim())) *sortOrder 
    return (Number(a) - Number(b)) *sortOrder 
    }
  }
table  {
  border-collapse : collapse;
  margin          : 2em 1em;
  font-family: Arial, Helvetica, sans-serif;
  }
td,th  {
  padding    : .2em .8em;
  border     : 1px solid darkblue;
  }
th::after {
  display    :  block;
  float      : inline-end;
  content    : '\25B7';
  margin     : 0 0 0 1em;
  transition : 180ms;
  color      : transparent;
}
th.asc::after {
  transform  : rotate(-90deg);
  color      : whitesmoke;
}
th.desc::after {
  transform  : rotate(+90deg);
  color      : whitesmoke;
}
thead {
  background : #437c97;
  color      : whitesmoke;
  cursor     : pointer;
  }
<table id="my-table" >
  <thead>
    <tr>
      <th data-type="num">ID</th> 
      <th data-type="str">Name</th>  
      <th data-type="num">Inventory Volume</th>  
    </tr>
  </thead>
  <tbody>
    <tr> <td>1</td> <td>Rachel</td> <td>Data is not enough</td> </tr>
    <tr> <td>2</td> <td>Ross</td>   <td>  100 </td>             </tr>
    <tr> <td>3</td> <td>Monica</td> <td>    1 </td>             </tr>
    <tr> <td>4</td> <td>Connor</td> <td>Data is not enough</td> </tr>
    <tr> <td>5</td> <td>Dustin</td> <td>   -5 </td>             </tr>
  </tbody>
</table>
Mister Jojo
  • 20,093
  • 6
  • 21
  • 40
0
  • This should work! Using @YamirL sort alghoritm.
function sort(e, method) {
  //Get table
  while ((e = e.parentElement) && !e.classList.contains("table"));
  //Get rows
  let rows = Array.from(e.getElementsByTagName("tr"));
  //Get each value for each row
  let values = rows.map(row => {
    let tds = Array.from(row.getElementsByTagName("td"));
    return tds.map(td => td.innerHTML);
  });
  /* 
  values is a 2D array which contains each row and column
  values = [
    [Name, Volume],               --> The headers need to be removed
    [Joey, Data Not Enough],
    [Ross,  -5],
    [Monica,    1],
    [Ben,   100],
    [Chandler, Data Not Enough]
  ];
  */
  values.shift(); //remove headers

  // Now we need to sort the array by volume
  values.sort((a, b) => {
    var exception = "Data Not Enough";
    if(a[1] == exception)
      return 1;
    if(b[1] == exception)
      return -1;
    return method == 'ascending' ? a[1] - b[1] : b[1] - a[1];
  });

  /*******     Put sort values on table     *************/

  // Get body
  let body = e.getElementsByTagName("tbody")[0];
  // Erase Body
  body.innerHTML = "";
  // Iterate each row
  values.forEach(row => {
    // Create new row element
    let tr = document.createElement("tr");
    // Iterate each column
    row.forEach(val => {
      // Create new value
      let td = document.createElement("td");
      // Append values
      td.append(val);
      tr.append(td);
    });
    // Append row to body
    body.append(tr);
  });
}

Here it is a snippet to try the complete code, I added a <tbody> to the table.

function sort(e, method) {
  while ((e = e.parentElement) && !e.classList.contains("table"));
  let rows = Array.from(e.getElementsByTagName("tr"));
  let values = rows.map(row => {
    let tds = Array.from(row.getElementsByTagName("td"));
    return tds.map(td => td.innerHTML);
  });

  values.shift(); 

  values.sort((a, b) => {
    var exception = "Data Not Enough";
    if(a[1] == exception)
      return 1;
    if(b[1] == exception)
      return -1;
    return method == 'ascending' ? a[1] - b[1] : b[1] - a[1];
  });

  
  let body = e.getElementsByTagName("tbody")[0];
  body.innerHTML = "";
  values.forEach(row => {
    let tr = document.createElement("tr");
    row.forEach(val => {
      let td = document.createElement("td");
      td.append(val);
      tr.append(td);
    });
    body.append(tr);
  });
}
.clickable {
  cursor: pointer;
}

.clickable:hover {
  opacity: 0.7;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>


<table class="table">
  <thead>
    <tr>
      <th scope="col">Name</th>
      <th scope="col" class="clickable" onclick='sort(this, "descending")'>Volume</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Joey</td>
      <td>Data Not Enough</td>
    </tr>
    <tr>
      <td>Ross</td>
      <td>-5</td>
    </tr>
    <tr>
      <td>Monica</td>
      <td>1</td>
    </tr>
    <tr>
      <td>Ben</td>
      <td>100</td>
    </tr>
    <tr>
      <td>Chandler</td>
      <td>Data Not Enough</td>
    </tr>
  </tbody>
</table>
AlexSp3
  • 2,201
  • 2
  • 7
  • 24