עמית לוי
עמית לוי

Reputation: 59

How to sort list by priority in JavaScript?

How to sort list items by priority? This is a to-do list. User can input an item, choose a priority, and add to list.

This is my HTML form:

<input id="task" type="text"/>

<select id="priority">
    <option id="Normal">Normal</option> 
    <option id="Urgent">Urgent</option>
    <option id="Critical">Critical</option>
    <option id="If You Can">If You Can</option>
</select>

<button onclick="amitFunction()">Add</button>

<hr/>

<table>
    <tr>
        <th id="result"></th>
        <th id="priorit"></th>
    </tr>
<table>

This is my JS:

 function amitFunction() {    
    /* Define vars and values  */
    var lia = document.createElement("p");
    var lib = document.createElement("p");
    var item = document.getElementById('task').value;
    var pro = document.getElementById('priority').value;
    var pro_array = ['Urgent','Critical','Normal'];
    var item_list = document.createTextNode(item);
    var item_pro = document.createTextNode(pro);

    lia.appendChild(item_list);
    lib.appendChild(item_pro);

    /*  Check if text is less than 6 chars or more than 42 chars  */
    if (item.length<6) {
        alert('Your text must have a least 6 chars');
    } else if (item.length>42) {
        alert('Your text must have less than 42 chars');
    } else {
        document.getElementById("result").appendChild(lia);
        document.getElementById("priorit").appendChild(lib);
        document.getElementById('task').value='';
    }

    /*  Change text color base on priority  */
    if (pro==pro_array[0]) {
        $("p:last-child").css('color','red');
    }
    if (pro==pro_array[1]) {
        $("p:last-child").css('color','orange');
    }
    if (pro==pro_array[2]) {
        $("p:last-child").css('color','green');
    }

    /*  Delete text when user clicks on it  */
    $([lia,lib]).click(function(){
        $([lia,lib]).css('color','gray');
        $([lia,lib]).css("text-decoration", "line-through");
    });
}

What I need is, when user adds a new item, it will sort by priority order.

  1. first : Urgent
  2. second : Critical
  3. third : Normal
  4. fourth : If You Can

Each new item that user adds, should be sorted like that. How can I do that?

This is the complete script (JSBin) to understand what I need.

Upvotes: 5

Views: 10120

Answers (2)

Lys
Lys

Reputation: 568

First of all I would suggest you create a new table row every time you create a TODO task, however I decided to keep as much of your code as I could and implement what you asked for. I will admit that it is not the best decision and could be optimized a lot, however I am leaving it as it is simply because there might be many interesting cases in the code that might teach you something new. The sorting is implemented. I hope this helps :)

Your html, left as it was:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>

</head>
<body>
    <script src="https://code.jquery.com/jquery-3.1.0.js"></script>

<input id="task" type="text"/>
<select id="priority">
 <option id="Normal">Normal</option> 
<option id="Urgent">Urgent</option>
<option id="Critical">Critical</option>
<option id="If You Can">If You Can</option>
 </select>

<button onclick="amitFunction()">Add</button>

<hr/>
<table>
  <tr>
    <th id="result"></th>
<th id="priorit"></th>
  </tr>
<table>
</body>
</html>

and the edited JS code:

//creating a global collection to hold your todo list in memory
var todo_list = [];

function amitFunction() {

        var item = document.getElementById('task').value;

        /*  Check if text is less than 6 chars or more than 42 chars 
            and return if validation is not passed */
        if(item.length<6){
             alert('Your text must have a least 6 chars');
             return;
        }else if(item.length>42){
            alert('Your text must have less than 42 chars');
            return; 
        }

        var pro = document.getElementById('priority').value;

        //keep this for colors
        var pro_array = ['Urgent','Critical','Normal'];

        //map string priorities to numeric values
        var priorities = 
            {
              'Urgent' : 0, 
              'Critical' : 1,
              'Normal' : 2,
              'If You Can' : 3
            }

        //push each new task in the todo list
        todo_list.push(
          {
            priority : pro,
            task : item
          }
        );

        //Now this here is perhaps the most important part,
        //this is where you sort your todo list based on the
        //mapped to numeric values priorities
        todo_list.sort(function (task1, task2) {
          return priorities[task1.priority] - priorities[task2.priority];
        });

        //clear the containers holding your list
        var resultNode = document.getElementById("result");

        while (resultNode.firstChild) {
          resultNode.removeChild(resultNode.firstChild);
        }

        var priorityNode = document.getElementById("priorit");

        while (priorityNode.firstChild) {
          priorityNode.removeChild(priorityNode.firstChild);
        }

        //recreate the DOM based on the todo_list collection
        for(var i =0; i < todo_list.length; i++)
          {
            var lia = document.createElement("p");
            var lib = document.createElement("p");

            var item_list = document.createTextNode(todo_list[i].task);
            var item_pro = document.createTextNode(todo_list[i].priority);

            lia.appendChild(item_list);
            lib.appendChild(item_pro);

            document.getElementById("result").appendChild(lia);
            document.getElementById("priorit").appendChild(lib);
            document.getElementById('task').value='';

              /*  Change text color base on priority  */
            if(todo_list[i].priority == pro_array[0]){
                 $("p:last-child").css('color','red');
               }
            if(todo_list[i].priority == pro_array[1]){
                 $("p:last-child").css('color','orange');
               }
             if(todo_list[i].priority == pro_array[2]){
                 $("p:last-child").css('color','green');
               }

         }

         //reinitialize the click handlers
         var resultNode = document.getElementById("result");
         var priorityNode = document.getElementById("priorit");

        for(var i =0; i< resultNode.childNodes.length; i++) (function(i){ 
          resultNode.childNodes[i].onclick = function() {
            $([resultNode.childNodes[i],priorityNode.childNodes[i]]).css('color','gray');
            $([resultNode.childNodes[i],priorityNode.childNodes[i]]).css("text-decoration", "line-through");
          }
          priorityNode.childNodes[i].onclick = function() {
            $([resultNode.childNodes[i],priorityNode.childNodes[i]]).css('color','gray');
            $([resultNode.childNodes[i],priorityNode.childNodes[i]]).css("text-decoration", "line-through");
          }
        })(i);

  }

And a working example here:

https://jsbin.com/kudipacexi/edit?html,js,output

In fact there are plenty of approaches, another approach would be to not keep a global collection for your list, instead do the sorting directly using the DOM elements, however you will still have to keep some kind of numeric representation of your priorities in order to sort them by priority. It might also be a good idea to subscribe each of the elements to a single click handler function, then add the line-through style based on the caller of the function. Another thing I'd suggest is, if you are involving jQuery and not focusing on just Vanilla JS, try and use jQuery for the majority of the DOM manipulation.

Upvotes: 4

gaetanoM
gaetanoM

Reputation: 42044

Because you have a table I suggest to use that as the output.

You can rearrange your select in order to add for each option a priority number and a color attribute like:

<option value="2" color="green">Normal</option>

The table can contain a first column as the current row priority. This column will be hidden.

Each time a new row must be added a sorting process is executed on table rows.

The snippet:

$('button').on('click', function (e) {
    var priorityValue = $('#priority option:selected').val();
    var priorityText = $('#priority option:selected').text();
    var colorVal = $('#priority option:selected').attr('color');
    var taskValue = $('#task').val();
    if (taskValue.length < 6) {
        $('#errMsg').text('Your text must have a least 6 chars');
        return;
    } else if (taskValue.length > 42) {
        $('#errMsg').text('Your text must have less than 42 chars');
        return;
    }
    $('#errMsg').text('');

    //
    // create the new table row...
    //
    var newRow = $('<tr/>', {style: 'color:' + colorVal})
            .append($('<td/>', {style: "display: none", text: priorityValue}))
            .append($('<td/>', {text: taskValue}))
            .append($('<td/>', {text: priorityText}));


    //
    // enlarge current table rows with the current one and sort elements
    //
    var tableRowsSorted = $('#result tbody').append(newRow).find('tr').get().sort(function(a, b) {
        var p1 = +$(a).find('td:first').text();
        var p2 = +$(b).find('td:first').text();
        return p1 - p2;
    });

    //
    // append/replace the taable body
    //
    $('#result tbody').append(tableRowsSorted);

    //
    // reset input text
    //
    $('#task').val('');
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>



<input id="task" type="text"/><span id="errMsg" style="color: red;"></span>
<select id="priority">
    <option value="2" color="green">Normal</option>
    <option value="0" color="red">Urgent</option>
    <option value="1" color="orange">Critical</option>
    <option value="3" color="black">If You Can</option>
</select>

<button>Add</button>

<hr/>
<table id="result">
    <thead>
    <tr>
        <th style="display: none">priority</th>
        <th>result</th>
        <th>priority</th>
    </tr>
    </thead>
    <tbody>

    </tbody>
</table>

Upvotes: 1

Related Questions