Reputation: 860
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.
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
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
Reputation: 24915
You can use a simple logic:
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
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
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
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