2

I am making an app in Electron and have a customer list with their name and location. I have a button that allows the user to add a customer, but when doing so the table shows the index. I have another button that sorts out the names in alphabetical order, but if there are rows that have the index in them, it displays those first...

What I would like is some restriction that puts the rows with (0)(1) at the end of the list when sorted rather than the beginning.

Example: adding customers sorting the customers

The customers are sorted correctly, but all the rows with 0 come before the rows with actual words when I would like to have the words before the 0's.

Code: for some reason in this code snippet it actually doesn't show the 0 or 1 index, but it still sorts the rows with nothing before the rows with text...

const back = document.getElementById('back');
const cust = document.getElementById('cust');
const custDiv = document.getElementById('custDiv');
const addCust = document.getElementById('addCust');
const inv = document.getElementById('inv');
const invDiv = document.getElementById('invDiv');
const addItem = document.getElementById('addItem');


// add customer
function appendRowCust() {
  var custList = document.getElementById('custList'), // table reference
    row = custList.insertRow(custList.rows.length), // append table row
    i;
  // insert table cells to the new row
  for (i = 0; i < custList.rows[0].cells.length; i++) {
    createCell(row.insertCell(i), i, 'row');
  }
}
// create DIV element and append to the table cell
function createCell(cell, text, style) {
  var div = document.createElement('div'), // create DIV element
    txt = document.createTextNode(''); // create text node
  div.appendChild(txt); // append text node to the DIV
  div.setAttribute('class', style); // set DIV class attribute
  div.setAttribute('className', style); // set DIV class attribute for IE (?!)
  cell.appendChild(div); // append DIV to the table cell
}


// sort customers 
function sortCustTable() {
  var custList, rows, switching, i, x, y, shouldSwitch;
  custList = document.getElementById("custList");
  switching = true;
  /* Make a loop that will continue until
  no switching has been done: */
  while (switching) {
    // Start by saying: no switching is done:
    switching = false;
    rows = custList.getElementsByTagName("TR");
    /* Loop through all table rows (except the
    first, which contains table headers): */
    for (i = 1; i < (rows.length - 1); i++) {
      // Start by saying there should be no switching:
      shouldSwitch = false;
      /* Get the two elements you want to compare,
      one from current row and one from the next: */
      x = rows[i].getElementsByTagName("TD")[0];
      y = rows[i + 1].getElementsByTagName("TD")[0];
      // Check if the two rows should switch place:
      if (x.innerHTML.toLowerCase() > y.innerHTML.toLowerCase()) {
        // I so, mark as a switch and break the loop:
        shouldSwitch = true;
        break;
      }
    }
    if (shouldSwitch) {
      /* If a switch has been marked, make the switch
      and mark that a switch has been done: */
      rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
      switching = true;
    }
  }
}
table {
  background-color: black;
  color: white;
} 

tr:nth-child(even) {
  background-color: #656565;
}
tr:nth-child(odd) {
  background-color: #505050;
}

td {
  width: 300px;
  max-width: 300px;
  height: 30px;
  text-align: center;
}
<div id="custDiv">
  <div class="addBtns">
    <button id="addCust" onclick="appendRowCust()">add customer</button>
  </div>

  <div style="width: 355px; margin: 0 auto; height: 50px;">
    <button id="sortCust" onclick="sortCustTable()">sort</button>
  </div>

  <div class="custScroll">
    <table id="custListTop">
      <tr>
        <td contenteditable="false">Customers</td>
        <td contenteditable="false">Main Location</td>
      </tr>
    </table>
    <table id="custList" contenteditable="true">
      <tr>
        <td>Someone</td>
        <td>something</td>
      </tr>
    </table>
  </div>

</div>

3 Answers 3

1

First, you need to summarize what your specifications are. To my understanding, they are this:

  • Your table needs to be sorted by the first column lexicographically...
  • Except for numeric or empty strings, which need to be ordered last in the column.

Now, the given answer by @IrkenInvader is correct in pointing out that you don't need to write your own sorting algorithm, but as far as implementing a correct and efficient solution, consider using the built-in algorithm for Array#sort() with some modifications:

function sortCustTable() {
  var custList = document.getElementById('custList');
  var rows = custList.getElementsByTagName('tr');
  var parent = rows[0].parentElement;
  var length = rows.length;
  var data = [], ref, charCodes;

  for (var index = 0; index < length; index++) {
    ref = {
      row: rows[index],
      value: rows[index].firstElementChild.textContent.toUpperCase()
    };

    if (ref.value === '') {
      ref.value = 'k'; // will sort after everything else, including numbers
    } else if (!isNaN(ref.value)) {
      charCodes = ref.value.split('').map(function (char) {
        return Number(char) + 97; // charCode for 'a'
      });

      // for example, '05' would become 'af'
      ref.value = String.fromCharCode.apply(String, charCodes);
    }

    data.push(ref);
  }

  data.sort(function (a, b) {
    if (a.value > b.value) return 1;
    if (a.value < b.value) return -1;
    return 0;
  });

  for (var index = 0; index < length; index++) {
    parent.appendChild(data[index].row);
  }
}

I opted to use only ECMAScript 5 features, since a comment in your code indicates wanting to support Internet Explorer. I notice you're using const though, so feel free to modify using ES6 if you feel that would be easier.

Putting this together with the rest of your code, you can see it working below. I added some more default values to the table to give you an idea of how well it works:

const back = document.getElementById('back');
const cust = document.getElementById('cust');
const custDiv = document.getElementById('custDiv');
const addCust = document.getElementById('addCust');
const inv = document.getElementById('inv');
const invDiv = document.getElementById('invDiv');
const addItem = document.getElementById('addItem');

// add customer
function appendRowCust() {
  var custList = document.getElementById('custList'), // table reference
    row = custList.insertRow(custList.rows.length), // append table row
    i;
  // insert table cells to the new row
  for (i = 0; i < custList.rows[0].cells.length; i++) {
    createCell(row.insertCell(i), i, 'row');
  }
}
// create DIV element and append to the table cell
function createCell(cell, text, style) {
  var div = document.createElement('div'), // create DIV element
    txt = document.createTextNode(''); // create text node
  div.appendChild(txt); // append text node to the DIV
  div.setAttribute('class', style); // set DIV class attribute
  div.setAttribute('className', style); // set DIV class attribute for IE (?!)
  cell.appendChild(div); // append DIV to the table cell
}


// sort customers 
function sortCustTable() {
  var custList = document.getElementById('custList');
  var rows = custList.getElementsByTagName('tr');
  var parent = rows[0].parentElement;
  var length = rows.length;
  var data = [], ref, charCodes;

  for (var index = 0; index < length; index++) {
    ref = {
      row: rows[index],
      value: rows[index].firstElementChild.textContent.toUpperCase()
    };

    if (ref.value === '') {
      ref.value = 'k'; // will sort after everything else, including numbers
    } else if (!isNaN(ref.value)) {
      charCodes = ref.value.split('').map(function (char) {
        return Number(char) + 97; // charCode for 'a'
      });

      // for example, '05' would become 'af'
      ref.value = String.fromCharCode.apply(String, charCodes);
    }

    data.push(ref);
  }

  data.sort(function (a, b) {
    if (a.value > b.value) return 1;
    if (a.value < b.value) return -1;
    return 0;
  });

  for (var index = 0; index < length; index++) {
    parent.appendChild(data[index].row);
  }
}
table {
  background-color: black;
  color: white;
} 

tr:nth-child(even) {
  background-color: #656565;
}
tr:nth-child(odd) {
  background-color: #505050;
}

td {
  width: 300px;
  max-width: 300px;
  height: 30px;
  text-align: center;
}
<div id="custDiv">
  <div class="addBtns">
    <button id="addCust" onclick="appendRowCust()">add customer</button>
  </div>

  <div style="width: 355px; margin: 0 auto; height: 50px;">
    <button id="sortCust" onclick="sortCustTable()">sort</button>
  </div>

  <div class="custScroll">
    <table id="custListTop">
      <tr>
        <td contenteditable="false">Customers</td>
        <td contenteditable="false">Main Location</td>
      </tr>
    </table>
    <table id="custList" contenteditable="true">
      <tr>
        <td>Someone</td>
        <td>something</td>
      </tr>
      <tr>
        <td>Somebody</td>
        <td>1</td>
      </tr>
      <tr>
        <td></td>
        <td>1</td>
      </tr>
      <tr>
        <td>0</td>
        <td>1</td>
      </tr>
      <tr>
        <td>someone else</td>
        <td>1</td>
      </tr>
      <tr>
        <td>somebody else</td>
        <td>1</td>
      </tr>
    </table>
  </div>

</div>

Now, to clarify why it sorts this way, let's look at the table values and how we modify them before sorting:

 Customers     | Main Location
---------------+---------------
 Someone       | something
 Somebody      | 1
               | 1
 0             | 1
 someone else  | 1
 somebody else | 1

We'll discard the second column since we don't use that, and set all the customers to uppercase as well:

 Customers     |
---------------|
 SOMEONE       |
 SOMEBODY      |
               |
 0             |
 SOMEONE ELSE  |
 SOMEBODY ELSE |

Next we check each string to see if it's empty, if so, we give it the value 'k':

 Customers     |
---------------|
 SOMEONE       |
 SOMEBODY      |
 k             |
 0             |
 SOMEONE ELSE  |
 SOMEBODY ELSE |

Then lastly, we modify any numbers by adding their numeric value to 97, and converting the resulting charCode into a character:

 Customers     |
---------------|
 SOMEONE       |
 SOMEBODY      |
 k             |
 a             |
 SOMEONE ELSE  |
 SOMEBODY ELSE |

Sorting lexicographically, we get:

 Customers     |
---------------|
 SOMEBODY      |
 SOMEBODY ELSE |
 SOMEONE       |
 SOMEONE ELSE  |
 a             |
 k             |

And putting back in the original values, we get:

 Customers     | Main Location
---------------+---------------
 Somebody      | 1
 somebody else | 1
 Someone       | something
 someone else  | 1
 0             | 1
               | 1
Sign up to request clarification or add additional context in comments.

Comments

1

I added a method to draw your table from an array of cell contents. Sorting the rows and calling drawTableRows will recreate the table in any order the rows array ends up. I added some code to insert mock data every third row so sorting numbers to the bottom can be seen.

This is a bigger change than I usually like to give in answers but I thought you might appreciate seeing a different approach.

var rows = [[ 'Someone', 'something' ]];

function drawTableRows() {
  var custList = document.getElementById('custList'); // table reference
  custList.innerHTML = '';
  
  for(var i = 0; i < rows.length; i++) {      
    var row = rows[i];
    var tableRow = custList.insertRow(i); // append table row    
        
    for(var j = 0; j < row.length; j++) {      
      createCell(tableRow.insertCell(j), row[j], 'row');      
    }
  }
}

// add customer
function appendRowCust(customer = 0, location = 1) {
  //throw in mock data every 3 rows (just a test - remove later)
  if(rows.length % 3 === 0) {
    customer = 'Real customer ' + rows.length;
    location = 'Real location ' + rows.length;
  }
  
  rows.push([customer, location]);
  drawTableRows();
}

// create DIV element and append to the table cell
function createCell(cell, text, style) {
  var div = document.createElement('div'), // create DIV element
    txt = document.createTextNode(text); // create text node
  div.appendChild(txt); // append text node to the DIV
  div.setAttribute('class', style); // set DIV class attribute
  div.setAttribute('className', style); // set DIV class attribute for IE (?!)
  cell.appendChild(div); // append DIV to the table cell
}

function sortCustTable() {
  rows.sort(function(a,b){
    //sort by first column
    var aVal = a[0];
    var bVal = b[0];
    
    //sort by cell content - if content is a number push to bottom.
    if((bVal > aVal) || !isNaN(bVal)) {
      return -1;
    }    
    if((aVal > bVal) || !isNaN(aVal)) {
      return 1;
    }    
    return 0;
  });
  
  drawTableRows();
}
table {
  background-color: black;
  color: white;
}

tr:nth-child(even) {
  background-color: #656565;
}

tr:nth-child(odd) {
  background-color: #505050;
}

td {
  width: 300px;
  max-width: 300px;
  height: 30px;
  text-align: center;
}
<div id="custDiv">
  <div class="addBtns">
    <button id="addCust" onclick="appendRowCust()">add customer</button>
  </div>

  <div style="width: 355px; margin: 0 auto; height: 50px;">
    <button id="sortCust" onclick="sortCustTable()">sort</button>
  </div>

  <div class="custScroll">
    <table id="custListTop">
      <tr>
        <td contenteditable="false">Customers</td>
        <td contenteditable="false">Main Location</td>
      </tr>
    </table>
    <table id="custList" contenteditable="true">
      <tr>
        <td>Someone</td>
        <td>something</td>
      </tr>
    </table>
  </div>

</div>

Comments

0

You can filter out the differences by attempting to cast to numeric and determining if the current iterated value is an integer. Afterwards simply sort and concatenate the two result sets.

 var list = [0, 2, "2", "0", 1, 2, "a", "b", "c"],
  numeric = list.filter(value => Number.isInteger(+value)),
  alpha = list.filter(value => !Number.isInteger(+value)),
  result = alpha.sort().concat(numeric.sort());

To optimize the above you can filter once and push to a separately declared array alpha if the result is false.

   var list = [0, 2, "2", "0", 1, 2, "a", "b", "c"],
   alpha = [],
   numeric = list.filter(value => {
     let torf = Number.isInteger(+value);
     if (!torf) alpha.push(value);
     return torf;
   }),
   result = alpha.sort().concat(numeric.sort());

Determining the difference between the two would be a micro-optimization that I doubt would be necessary in any situation and the former is more verbose and clear. My suggestion would be to use the first option.

      var list = [0, 2, "2", "0", 1, 2, "a", "b", "c"],
      numeric = list.filter(value => Number.isInteger(+value)),
      alpha = list.filter(value => !Number.isInteger(+value)),
      result = alpha.sort().concat(numeric.sort());
    
    console.log(result);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.