Owais
Owais

Reputation: 860

sort array based on conditions using javascript sort function

I want to sort an javascript array based on numbers but I wanted a specific group (e.g. Recommended) comes first and ascending, followed by other group that has no types.

Below are the divs, I wanted to sort in such a way that the Recommended comes first and in ascending order followed by other divs in ascending order.

enter image description here

Below code is sorting the divs with no data-type in ascending order correctly but the Recommended is not sorting in ascending order as expected.

var sortedDivs = $(".terminalpopular").toArray().sort(sorter);
console.log(sortedDivs); 

function sorter(a, b) {
    if(a.getAttribute('data-type') == 'Recommended'){
        if (parseFloat(a.getAttribute('data-amount')) < parseFloat(b.getAttribute('data-amount'))){
          return -1;
        }
        return 0;
    }
    else{
      if (parseFloat(a.getAttribute('data-amount')) > parseFloat(b.getAttribute('data-amount'))){
        return 1;
      }
      return 0;
    }
  }

Above code sorts the divs in such form as;

<div class="hotel-list listing-style3 hotel" id="list_view_listingpopular">
    <div class="terminalpopular" data-amount="65.95" data-type="Recommended">
    </div>
    <div class="terminalpopular" data-amount="94.95" data-type="Recommended">  </div>
    <div class="terminalpopular" data-amount="70" data-type="Recommended">
    </div>
    <div class="terminalpopular" data-amount="54.95" data-type="">
    </div>
    <div class="terminalpopular" data-amount="67.99" data-type="">
    </div>
    <div class="terminalpopular" data-amount="75" data-type="">
    </div>
    <div class="terminalpopular" data-amount="78" data-type="">
    </div>
    <div class="terminalpopular" data-amount="84.99" data-type="">
    </div>
    <div class="terminalpopular" data-amount="84.99" data-type="">
    </div>
    </div>

As, it can be seen that divs without any data-type is sorted correctly but Recommended divs are not sorted in ascending order as expected.

Note: It does come in IF clause and condition does meet i.e. (if(a.getAttribute('data-type') == 'Recommended'){}). I have put console in IF and it does compare values.

Please, help. Thanks

Upvotes: 3

Views: 3704

Answers (5)

Thomas
Thomas

Reputation: 12637

thanks to type coercion in JS you can use a Boolean (like in a comparison) as a Number. And the same with the Strings. Since - is a numeric operation, the strings will be casted to Numbers, too.

So you can treat this like a simple numeric sort on two "properties".

function sorter(a, b) {
    return (b.dataset.type === 'Recommended') - (a.dataset.type === 'Recommended') 
        || a.dataset.amount - b.dataset.amount;
}

var sortedDivs = $(".terminalpopular").toArray().sort(sorter);

console.log(sortedDivs)
.as-console-wrapper{top:0;max-height:100%!important}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="hotel-list listing-style3 hotel" id="list_view_listingpopular">
  <div class="terminalpopular" data-amount="65.95" data-type="Recommended">
  </div>
  </div>
  <div class="terminalpopular" data-amount="54.95">
  </div>
  <div class="terminalpopular" data-amount="70" data-type="Recommended">
  <div class="terminalpopular" data-amount="84.99">
  </div>
  <div class="terminalpopular" data-amount="94.95" data-type="Recommended"> </div>
  <div class="terminalpopular" data-amount="75">
  </div>
  <div class="terminalpopular" data-amount="67.99">
  </div>
  <div class="terminalpopular" data-amount="78">
  </div>
  <div class="terminalpopular" data-amount="84.99">
  </div>
</div>

Upvotes: 4

Rajesh
Rajesh

Reputation: 24915

You can use a simple logic:

  • Create 4 flags, 2 for element is recommended and 2 for amount value
  • Since recommended has higher precedence than amount, it should come first.
  • If both are recommended, check for amount

function sortList() {
  var list = $('.terminalpopular');
  var clone = list.clone();
  var recommended = 'Recommended'
  clone.sort(function(a, b) {
    var isARecommended = $(a).data('type') === recommended ? -10 : 0;
    var isBRecommended = $(b).data('type') === recommended ? -10 : 0;

    var amountA = Number($(a).data('amount'));
    var amountB = Number($(b).data('amount'));

    return isARecommended - isBRecommended || amountA - amountB;
  });
  list.parent().empty().append(clone)
}

// This is only for display purpose
function displayValue() {
  $('.terminalpopular').each(function(i, el) {
    $(el).text($(el).data('amount'));
    if ($(el).data('type')) {
      $(el).addClass('highlight')
    }
  })
}

displayValue();
sortList();
.terminalpopular {
  height: 30px;
  border: 1px solid gray;
}

.highlight {
  background: #ddd;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div class="hotel-list listing-style3 hotel" id="list_view_listingpopular">
  <div class="terminalpopular" data-amount="65.95" data-type="Recommended">
  </div>
  <div class="terminalpopular" data-amount="94.95" data-type="Recommended"> </div>
  <div class="terminalpopular" data-amount="70" data-type="Recommended">
  </div>
  <div class="terminalpopular" data-amount="54.95" data-type="">
  </div>
  <div class="terminalpopular" data-amount="67.99" data-type="">
  </div>
  <div class="terminalpopular" data-amount="75" data-type="">
  </div>
  <div class="terminalpopular" data-amount="78" data-type="">
  </div>
  <div class="terminalpopular" data-amount="84.99" data-type="">
  </div>
  <div class="terminalpopular" data-amount="84.99" data-type="">
  </div>
</div>

Upvotes: 0

A. Iglesias
A. Iglesias

Reputation: 2675

I don't know if it can be done in just one sort, but with two, this is working for me...

var myList = $(".terminalpopular");

myList.sort(function(a, b){
    return $(a).data("amount")-$(b).data("amount");
}).sort(function(a, b){
    if ($(a).data("type") == "Recommended" && !$(b).data("type"))
        return -1;
    else if (!$(a).data("type") && $(b).data("type") == "Recommended")
        return 1;
    else
        return 0;
});

$("#list_view_listingpopular").html(myList);

Here you have a fiddle... https://fiddle.jshell.net/rigobauer/c47Lfq8c/

I hope it helps.

Upvotes: 0

dev8080
dev8080

Reputation: 4020

If elements of a type are to be ordered amongst themselves before the others, you should probably separate them in an array and sort them there, merging them wiht the rest of the sorted elements later.

Try this:

 var sortedDivs1 = $(".terminalpopular").toArray().filter(function(){
               return $(this).attr('data-type') == 'Recommended'
            }).sort(sorter);
 var sortedDivs2 = $(".terminalpopular").toArray().filter(function(){
               return $(this).attr('data-type') != 'Recommended'
            }).sort(sorter);
var sortedDivs = $.merge(sortedDivs1, sortedDivs2);
console.log(sortedDivs); 

function sorter(a, b) {

        return parseFloat(a.getAttribute('data-amount')) - 
               parseFloat(b.getAttribute('data-amount'));
 }

Upvotes: 2

whymatter
whymatter

Reputation: 775

You have to return 0 only when both are equal.

Use this at both places where you compare the amount attribute:

return parseFloat(a.getAttribute('data-amount')) - parseFloat(b.getAttribute('data-amount'))

Upvotes: 0

Related Questions