1

I've an array which is being populated by options in select dropdowns.

jQuery('.wcpf-input-drop-down').each(function () {
var sel = jQuery(this);
var selected = sel.val(); // cache selected value, before reordering
var opts_list = sel.find('option');
opts_list.sort(function(a, b) { return jQuery(a).text() > jQuery(b).text() ? 1 : -1; });
sel.html('').append(opts_list);
sel.val(selected); // set cached selected value
    });

The above code is from Sorting options elements alphabetically using jQuery, written by Malak George. EDIT: I've modified it to look for all instances separately.

Now, this is working fine as far as finding the instances of .wcpf-input-drop-down and sorting the options within.

However, this sorting produces ordering in this fashion: "1, 2, 26, 27, 28, 3, 31" and so on.

I would need it to sort as follows: "1, 2, 3, 26, 27, 28, 31".

I've been searching for hours and found plenty of talk on the subject, but I cannot get to where I want. I'm sure I've seen ways to accomplish what I need, but haven't been able to. I think here's one that I could use, if I'd only know how to insert that into my existing code: Sort Array Elements (string with numbers), natural sort

Could someone help me implement the sorting I wish to see into the code I have now?

Thank you.

EDIT2: Here's a pen with all the relevant data to this question: https://codepen.io/jpontinen/pen/NJrXKQ

  • What part of the options are you sorting by? value or text? – zer00ne Mar 03 '19 at 13:41
  • Can you share associated HTML too, If possible can you share in a codepen/jsbin or something similar. As far as I know this is happening because text() method would return string, you would need to convert it into integer by parseInt() method. I can look into code if you can share it in bin. – Bhavesh Gupta Mar 03 '19 at 14:19
  • Thank you for your responses. Here's a pen with all the relevant data: https://codepen.io/jpontinen/pen/NJrXKQ – Jaakko Pöntinen Mar 04 '19 at 05:55
  • It's because using `jQuery(a).text()` you get string. comparing strings(that are really numbers) is different than comapring numbers. for example, comapring strings '26' > '3' will return false. 26 > 3 will return true – Sylwek Mar 04 '19 at 06:50

3 Answers3

1

Subtract the options in your sorting callback, that way they are automatically coerced to number data type:

opts_list.sort(function(a, b) { 
     return jQuery(a).text() - jQuery(b).text()
});

If you also have drop-down lists that contain non-numerical data, that need alphabetical sorting, then you need a natural sort method:

opts_list.sort(function(a, b) { 
  return jQuery(a).text().localeCompare(jQuery(b).text(), undefined, {numeric: true, sensitivity: 'base'});
});
trincot
  • 317,000
  • 35
  • 244
  • 286
  • Ah, sorry, forgot to mention - I used the alphabetical sorting one, for I don't know exactly what my client will input. The options are, in practice, about vehicle tires/wheels and they can contain letters as well. – Jaakko Pöntinen Mar 04 '19 at 06:59
  • Please consider giving my question a +1, I think I asked in a SO-approved way. Thank you! – Jaakko Pöntinen Mar 04 '19 at 07:30
0

The problem is you are sorting strings lexicographically ("26" comes before "3").

You want to sort by numeric value (by converting the strings to numbers):

// Implicitly cast to Number type using subtraction operator.
opts_list.sort(function(a, b) {
    return jQuery(a).text() - jQuery(b).text();
});

Here are some other variations:

// Fewest changes to original version in question.
// Explicit conversion using parseInt().
opts_list.sort(function(a, b) {
    // Should return 0 when a and b are equal, but this version returns -1.
    return parseInt(jQuery(a).text()) > parseInt(jQuery(b).text()) ? 1 : -1;
});
// Explicitly convert to integer type using parseInt.
// Note `jQuery(a).text()` can be simplified to `a.text`
opts_list.sort(function(a, b) {
    return parseInt(a.text, 10) - parseInt(b.text, 10);
});
Leftium
  • 16,497
  • 6
  • 64
  • 99
  • Thank you. I'm sure these options will benefit many. The answers I found on SO pre-my question were incredibly stretched, 10-40-lines of code with links to external plugins and all sorts of shenanigans. Please consider giving my question a +1, I think I asked in a SO-approved way. – Jaakko Pöntinen Mar 04 '19 at 07:31
  • Last option is not correct. It should use `textContent` – trincot Mar 04 '19 at 07:35
0

Details are commented in demo.

/* 
On each select.sel...
...get all of its options...
...then make an array of those option.
Next, sort the array of options by their .value...
...and remove everything in select.sel...
...and append the sorted array in select.sel
*/
$('.sel').each(function(i) {
  var opt = $(this).find('option');
  var arr = $.makeArray(opt);
  var sorted = arr.sort(function(a, b) {
    return parseInt(a.value, 10) - parseInt(b.value, 10);
  });
  $(this).empty().append(sorted);
});
<select class="sel">
  <option value="0">0</option>
  <option value="1">1</option>
  <option value="10">10</option>
  <option value="107">107</option>
  <option value="108">108</option>
  <option value="11">11</option>
  <option value="75">75</option>
  <option value="8">8</option>
  <option value="83">83</option>
  <option value="9">9</option>
  <option value="110">110</option>
  <option value="111">111</option>
  <option value="114">114</option>
  <option value="12">12</option>
  <option value="71">71</option>
  <option selected>Values to be sorted</option>
</select>

<hr>

<select class="sel">
  <option value="11">11</option>
  <option value="89">89</option>
  <option value="5">5</option>
  <option value="0">0</option>
  <option value="46">46</option>
  <option value="33">33</option>
  <option value="1">1</option>
  <option value="6">6</option>
  <option value="21">21</option>
  <option value="40">40</option>
  <option value="24">24</option>
  <option value="175">175</option>
  <option value="81">81</option>
  <option value="123">123</option>
  <option value="94">94</option>
  <option selected>Values to be sorted</option>
</select>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
zer00ne
  • 41,936
  • 6
  • 41
  • 68