Tenaciousd93
Tenaciousd93

Reputation: 3488

Multiple Sort in List.js

I'm using List.js to display, search, filter, a lot of contents in custom order.

My row is made like this

<div class="content-row">
    <div class="order_id" style="display:none;">{{$item['order']}}</div>
    <div class="order_category" style="display:none;">99999</div>
    <div class="order_source" style="display:none;">99999</div>
    <div class="order_page" style="display:none;">99999</div>
    <div class="content_date" style="display:none;">2016-11-08 11:00:00</div>
    <div class="dd-handle row-container"> I am a row </div>
</div>

I'm trying to order my list of content by multiple order: first order by order_category, then by order_source, then by content_date, and finally by order_page.

In Firefox I tried to order list from less important to more important order and it seems to work, but this didn't work in Chrome:

// Options
var options = {
    valueNames: ['content_id', 'content_date', 'order_id', 'order_category', 'order_source', 'order_page']
};
// Get list Html
var listElement = $("#main_list");
// Init list obj
var listInstance = new List(listElement[0], options);

# ....

// This works in Firefox but not in Chrome!

// Order by page
listInstance.sort('order_page', {
    order: "asc"
});
// then order by date
listInstance.sort('content_date', {
    order: "desc"
});
// then order by list
listInstance.sort('order_source', {
    order: "asc"
});
// finally order by category
listInstance.sort('order_category', {
    order: "asc"
});

# ....

I know that List.js doesn't support multiple sort now but I read that supports custom sort function. Could anyone teach/show me how to build a custom sort function?


Edit:

Thanks to @Dekel I build my own method that use a List.js with custom sort function with 4 level of sort. It works good with a single sort type (ASC or DESC) specified to List.js. I report my code:

/**
 * Sort a List.js list by 4 level of sort
 *
 * @param  {Object} list        [List.js instance]
 * @param  {String} first       [First field to sort]
 * @param  {String} second      [Second field to sort]
 * @param  {String} third       [Third field to sort]
 * @param  {String} fourth      [Fourth field to sort]
 * @param  {String} sort        [sort type: asc || desc]
 * @return {}                   []
 */
var sortList = function(list, first, second, third, fourth, sort) {

    // If list not defined
    if (Utility.empty(list)) {
        console.error("ListManager: can't sort, list is undefined!");
        // Error no list!
        return false;
    }

    // If first order id not defined
    if (Utility.empty(first)) {
        // Set default
        first = "name";
    }

    // If second order id not defined
    if (Utility.empty(second)) {
        // Set default
        second = "born";
    }

    // If third order id not defined
    if (Utility.empty(third)) {
        // Set default
        third = "color";
    }

    // If fourth order id not defined
    if (Utility.empty(fourth)) {
        // Set default
        fourth = "lastName";
    }

    // Check order is not defined
    if (Utility.empty(sort)) {
        // Set default order type to desc 
        sort = "desc";
    }

    // Make list order compatible with list.js fields
    first = "order_" + first;
    second = "order_" + second;
    third = "order_" + third;
    fourth = "order_" + fourth;

    console.log("List Sort: ", first, second, third, fourth);

    // Call sort method of List.js
    list.sort('', {order: sort,
        sortFunction:
            function(a, b) {
                // Compare values with field requested
                return _compareIntegerStringDate(a, b, first)
                    || _compareIntegerStringDate(a, b, second)
                    || _compareIntegerStringDate(a, b, third)
                    || _compareIntegerStringDate(a, b, fourth);
            }
        }
    );
}


/**
 * Compare list.js items value based on filed request.
 * Get correct comparison between integers, strings, or dates.
 *
 * @param  {Object}  a           [List.js item instance]
 * @param  {Object}  b           [List.js item instance]
 * @param  {String}  field       [Field to compare]
 * @return {Integer}             [-X || 0 || +X]
 */
var _compareIntegerStringDate = function(a, b, field) {
    if (Utility.isInt(a.values()[field])) {
        // Compare integer
        return  b.values()[field] - a.values()[field];
    }
    else if(Utility.isDate(a.values()[field], "YYYY-MM-DD HH:mm:ss")){
        // Compare Date
        return Date.parse(b.values()[field]) - Date.parse(a.values()[field]);
    }
    else {
        // Compare strings
        return a.values()[field].localeCompare(b.values()[field]);
    }
}

But if I try to specify the sort type (ASC or DESC) to each value it din't work. I report my code updated with this improvement:

/**
 * Sort a List.js list by 4 level of sort
 *
 * @param  {Object} list        [List.js instance]
 * @param  {String} first       [First field to sort]
 * @param  {String} second      [Second field to sort]
 * @param  {String} third       [Third field to sort]
 * @param  {String} fourth      [Fourth field to sort]
 * @param  {String} firstOrder  [Order type: asc || desc]
 * @param  {String} secondOrder [Order type: asc || desc]
 * @param  {String} thirdOrder  [Order type: asc || desc]
 * @param  {String} fourthOrder [Order type: asc || desc]
 * @return {}              []
 */
var sortList = function(list,
    first, second, third, fourth,
    firstOrder, secondOrder, thirdOrder, fourthOrder,
) {
    console.log("ListManager sort:", list, first, second, third, fourth);
    // If list not defined
    if (Utility.empty(list)) {
        console.error("ListManager: can't sort, list is undefined!");
        // Error no list!
        return false;
    }

    // If first order id not defined
    if (Utility.empty(first)) {
        // Set default
        first = "name";
    }

    // If second order id not defined
    if (Utility.empty(second)) {
        // Set default
        second = "born";
    }

    // If third order id not defined
    if (Utility.empty(third)) {
        // Set default
        third = "color";
    }

    // If fourth order id not defined
    if (Utility.empty(fourth)) {
        // Set default
        fourth = "lastName";
    }

    // Check order if asc or desc
    if (Utility.empty(firstOrder)) {
        // Set order
        firstOrder = "desc";
    }

    // Check order if asc or desc
    if (Utility.empty(secondOrder)) {
        // Set order
        secondOrder = "desc";
    }

    // Check order if asc or desc
    if (Utility.empty(thirdOrder)) {
        // Set order
        thirdOrder = "desc";
    }

    // Check order if asc or desc
    if (Utility.empty(fourthOrder)) {
        // Set order
        fourthOrder = "desc";
    }

    // Make list compatible
    first = "order_" + first;
    second = "order_" + second;
    third = "order_" + third;
    fourth = "order_" + fourth;
    // Make ascending descending compatible
    firstAsc = firstOrder === "asc" ? true : false;
    secondAsc = secondOrder === "asc" ? true : false;
    thirdAsc = thirdOrder === "asc" ? true : false;
    fourthAsc = fourthOrder === "asc" ? true : false;

    console.log("List Sort: ", first, second, third, fourth);
    console.log("List Asc: ", firstAsc, secondAsc, thirdAsc, fourthAsc);

    // Call sort method of List.js
    list.sort('', {order: '',
        sortFunction:
            function(a, b) {
                // Compare values with field requested
                return _compareIntegerStringDate(a, b, first, firstAsc)
                    || _compareIntegerStringDate(a, b, second, secondAsc)
                    || _compareIntegerStringDate(a, b, third, thirdAsc)
                    || _compareIntegerStringDate(a, b, fourth, fourthAsc);
            }
        }
    );
}

/**
 * Compare list.js items value based on filed request.
 * Get correct comparison between integers, strings, or dates.
 *
 * @param  {Object}  a           [List.js item instance]
 * @param  {Object}  b           [List.js item instance]
 * @param  {String}  field       [Field to compare]
 * @param  {Boolean} isAscending [Determinate if is ascending order]
 * @return {Integer}             [-X || 0 || +X]
 */
var _compareIntegerStringDate = function(a, b, field, isAscending) {
    console.log(field + " isAscending " + isAscending);
    if (Utility.isInt(a.values()[field])) {
        // Compare integer
        return isAscending
            ? a.values()[field] - b.values()[field]
            : b.values()[field] - a.values()[field];
    }
    else if(Utility.isDate(a.values()[field], "YYYY-MM-DD HH:mm:ss")){
        // Compare Date
        return isAscending
            ? Date.parse(a.values()[field]) - Date.parse(b.values()[field])
            : Date.parse(b.values()[field]) - Date.parse(a.values()[field]);
    }
    else {
        // Compare strings
        return isAscending
            ? b.values()[field].localeCompare(a.values()[field])
            : a.values()[field].localeCompare(b.values()[field]);
    }
}

Any suggestions to fix that?

Thank you very much!

Upvotes: 3

Views: 3330

Answers (1)

Dekel
Dekel

Reputation: 62566

List.js support writing your own sort function:

list.sort('name', {sortFunction: function() { ... })

Basically the sort function gets two parameters - two elements from the list, and compare them. If the first element should be first, the function should return -1. If the second item should be first, the function should return 1. If they are equal, the function should return 0.

The List.js library gives you all the value-names inside the a.values(), so you can use them for your comparison.

In the following example I created a list with names and years and the sort function sort the by name (asc) and year (desc).

var options = {
  valueNames: [ 'name', 'born' ]
};

var usersList = new List('users', options);
usersList.sort('', {order: "desc", sortFunction: 
                    function(a, b) {
                      if (Math.abs(b.values().name.localeCompare(a.values().name)) == 1) {
                        return b.values().name.localeCompare(a.values().name)
                      } else {
                        return a.values().born - b.values().born;
                      }
                    }
                   })
<script src="//cdnjs.cloudflare.com/ajax/libs/list.js/1.3.0/list.min.js"></script>

<div id="users">

  <input class="search" placeholder="Search" />
  <button class="sort" data-sort="name">
    Sort
  </button>

  <ul class="list">
    <li>
      <h3 class="name">AAA</h3>
      <p class="born">1986</p>
    </li>
    <li>
      <h3 class="name">AAB</h3>
      <p class="born">1985</p>
    </li>
    <li>
      <h3 class="name">AAC</h3>
      <p class="born">1986</p>
    </li>
    <li>
      <h3 class="name">AAB</h3>
      <p class="born">1983</p>
    </li>
  </ul>

</div>

Upvotes: 2

Related Questions