-1

I have an array of objects that I need to sort in javascript (es6 is fine), or jquery. The data is a bit more complicated than a regular array of objects because the value is located in a sub-object. I need to sort for a dynamic key where the data to sort on is located in an object of the dynamic key. For example I need to sort 'id' ascending or descending and the data is located in id.data.

    [{
    "_row": {},
    "_parent": {},
    "id": {"data": 112, "cell": {}},
    "name": {"data": "D'Amore, Volkman and Cole", "cell": {}},
    "check_name": {"data": "", "cell": {}},
    "account_number": {"data": "5534867831801846", "cell": {}},
    "main_email": {"data": "akovacek@yahoo.com", "cell": {}},
    "cc_email": {"data": "cupton@gmail.com", "cell": {}},
    "main_phone": {"data": "1-845-550-6422", "cell": {}},
    "work_phone": {"data": "+1 (859) 399-6372", "cell": {}},
    "mobile": {"data": "292-242-7626 x798", "cell": {}},
    "fax": {"data": "", "cell": {}},
    "active": {"data": 1, "cell": {}},
    "billing_address": {"data": "24226 Mackenzie Junctions Suite 393\nDonaldside, GA 87531", "cell": {}},
    "shipping_address": {"data": "478 Toy Loaf Suite 552\nWaelchiberg, ND 70701-3633", "cell": {}},
    "comments": {"data": "", "cell": {}}
}, {
    "_row": {},
    "_parent": {},
    "id": {"data": 120, "cell": {}},
    "name": {"data": "Carroll, Rice and Reilly", "cell": {}},
    "check_name": {"data": "", "cell": {}},
    "account_number": {"data": "4539358256447", "cell": {}},
    "main_email": {"data": "ocie.ebert@bergstrom.net", "cell": {}},
    "cc_email": {"data": "bmoen@kshlerin.info", "cell": {}},
    "main_phone": {"data": "612-864-9512", "cell": {}},
    "work_phone": {"data": "(519) 761-1805", "cell": {}},
    "mobile": {"data": "+1-730-669-4339", "cell": {}},
    "fax": {"data": "", "cell": {}},
    "active": {"data": 1, "cell": {}},
    "billing_address": {"data": "848 Rashawn Causeway\nHauckberg, GA 21193", "cell": {}},
    "shipping_address": {"data": "3458 Wolff Cape Suite 336\nWolfmouth, DC 35821", "cell": {}},
    "comments": {"data": "", "cell": {}}
}, {
    "_row": {},
    "_parent": {},
    "id": {"data": 122, "cell": {}},
    "name": {"data": "Denesik and Sons", "cell": {}},
    "check_name": {"data": "", "cell": {}},
    "account_number": {"data": "6011079688853496", "cell": {}},
    "main_email": {"data": "clinton41@schiller.com", "cell": {}},
    "cc_email": {"data": "daron80@corwin.info", "cell": {}},
    "main_phone": {"data": "569-382-2580 x1764", "cell": {}},
    "work_phone": {"data": "705.782.2219", "cell": {}},
    "mobile": {"data": "936-586-1978", "cell": {}},
    "fax": {"data": "", "cell": {}},
    "active": {"data": 1, "cell": {}},
    "billing_address": {"data": "1864 Donnelly Parkway Suite 222\nPort Hailieburgh, NC 08808-0938", "cell": {}},
    "shipping_address": {"data": "28476 Jerald Valleys Apt. 537\nNorth Vancemouth, DC 16865-0793", "cell": {}},
    "comments": {"data": "", "cell": {}}
}, {
    "_row": {},
    "_parent": {},
    "id": {"data": 124, "cell": {}},
    "name": {"data": "Trantow, Botsford and Runolfsson", "cell": {}},
    "check_name": {"data": "", "cell": {}},
    "account_number": {"data": "4556163511909216", "cell": {}},
    "main_email": {"data": "jordane77@adams.com", "cell": {}},
    "cc_email": {"data": "shawn34@block.info", "cell": {}},
    "main_phone": {"data": "+16989316200", "cell": {}},
    "work_phone": {"data": "969.610.8041 x8593", "cell": {}},
    "mobile": {"data": "680.717.5368", "cell": {}},
    "fax": {"data": "", "cell": {}},
    "active": {"data": 1, "cell": {}},
    "billing_address": {"data": "96778 VonRueden Square Suite 421\nKennafort, SC 70938", "cell": {}},
    "shipping_address": {"data": "13334 Orion Green\nEast Lillieborough, ND 19714", "cell": {}},
    "comments": {"data": "", "cell": {}}
}]

The next step of complication is that I would like to sort by id, then another key, like active, then name, etc.

Any Ideas? Can I harness .sort? (it looks like I am not using the cell object, maybe I can remove that to make my life easier. I think I added it for a very important reason years ago)

Here is an update to what I had figured out.. the first sort works fine, sorting multiple columns does not appear to be working.

attempt 1:

        //sort_array looks like [{db_field:'asc'},etc...]
    //we need to sort row based on data for this.tdo[row][db_filed]['data']
    this.tdo.sort((a,b) => {
        sort_array.forEach(sort => {
            let keys = Object.keys(sort);
            let name = keys[0];
            if(sort[keys[0]] =='asc')
            {
                this.tdo = this.tdo.sort(this.dynamicSort(name));
            }
            else
            {
                this.tdo = this.tdo.sort(this.dynamicSort('-'+name));
            }
        })

    })

where dynamicSort was pulled from stack overflow Sort array of objects by string property value in JavaScript

dynamicSort(property) {
    let sortOrder = 1;
    if (property[0] === "-") {
        sortOrder = -1;
        property = property.substr(1);
    }
    return function (a, b) {
        let result = (a[property].data < b[property].data) ? -1 : (a[property].data > b[property].data) ? 1 : 0;
        return result * sortOrder;
    }
}

attempt 2 , using thenBy which is pretty cool. I found an example how to stack sorting in their closed issues:

 let sort_stack = firstBy(function (v1, v2) { return 0 });
    sort_array.forEach(sort => {
        let keys = Object.keys(sort);
        let name = keys[0];
        if(sort[keys[0]] =='asc')
        {
            sort_stack = sort_stack.thenBy(function (v1) { return v1[name].data; });
        }
        else
        {
            sort_stack = sort_stack.thenBy(function (v1) { return v1[name].data ; },-1);
        }
    })

    this.tdo.sort(sort_stack);

Additionally I may need to restrict user input sub-sorting based on the type of data... as columns like id will sort only once

Not sure why I got a down vote, this sort is pretty complicated and beyond the scope of the .sort documentation

Community
  • 1
  • 1
Iannazzi
  • 1,348
  • 2
  • 18
  • 27

2 Answers2

0

You can use a chained sort() on the array using custom comparing functions. For instance, if the array is named "items" you can use the following base code:

function compare(value1, value2){
  if(value1 < value2) return -1;
  else if(value1 == value2) return 0;
  else return 1;
}

items = items.sort(function(item1, item2){ // First sorting criteria, e.g. by id.data
  return compare(item1.id.data, item2.id.data);
}).sort(function(item1, item2){ // Second sorting criteria, e.g. by active.data
  return compare(item1.active.data, item2.active.data);
}).sort(function(item1, item2){  // Third sorting criteria, e.g. by name.data
  return compare(item1.name.data, item2.name.data);
});

UPDATED ANSWER:

OK, it seems I misunderstood you, my bad. I now see you want to sort the items by one field, and then those sorted items, by a separate column but by retaining the previous sorting, in a database-like fashion.

To do this you need to group the sorted items into sub-arrays, and then sort them individually by your second criteria. I've created a fiddle for you:

https://jsfiddle.net/d8vzn9b0/1/

And here's the main code that's being used to accomplish this:

function compare(value1, value2){
  if(value1 < value2) return -1;
  else if(value1 == value2) return 0;
  else return 1;
}

function splitItemsByFieldValue(items, field){
  var splittedByValue = {};
  var key;

  // Group items in an object-based hash indexed by the field value
  for(var item of items){
    // We use a string prefix to make all object keys strings to prevent the "empty slots" in Javascript object or array based hashes
    key = 'value_' + item[field].data;
        if(!splittedByValue[key]) splittedByValue[key] = [];
    splittedByValue[key].push(item);
  }

  // Convert the indexed array (grouped items) to a flat array (not associative) of the grouped items as subarrays
  var splittedFlat = [], subarray;

  for(key in splittedByValue){
    subarray = splittedByValue[key];
    splittedFlat.push(subarray);
  }

  return splittedFlat;
}

var sortedItems = [];

var sortedByActive = items.sort(function(item1, item2){ // First sorting criteria, e.g. by active.data
  return compare(item1.active.data, item2.active.data);
});

for(var subarray of splitItemsByFieldValue(sortedByActive, 'active')){
  var sortedSubArray = subarray.sort(function(item1, item2){  // Second sorting criteria, e.g. by name.data
    return (item1.name && (typeof item1.name.data) == 'string') ? item1.name.data.localeCompare(item2.name.data) : 1;
  });

  for(var item of sortedSubArray){
    sortedItems.push(item);
  }
}

Note that I created a splitItemsByFieldValue() function to allow you split the sorted items into subarrays by a particular field. You can use this code as a basis for adding more sorting criteria on top. Also, note that I'm using localeCompare() for sorting by name, so that a natural sorting (for text, rather than numbers) comparision is used, while a standard numeric comparision is used for the other fields like active.

BTW, there's no need to sort by "ID", as IDs will always be (should be) unique. You cannot have 2 sorting criterias at the same time that can have an indefinite amount of discrete values, otherwise the only criteria that will be applied is the first one and the other ones will be ignored. For example, if you first sort by ID then it's unnecessary to sort by active, and then by field, as the results will be still sorted by ID.

When you use sorting, you should first sort by columns that have a finite list of discrete values, and after that then you can sort by columns that may have continuous (e.g. a price, amount, etc.) or indefinite discrete values (e.g. a unique ID).

ablopez
  • 850
  • 4
  • 7
  • Thanks, I have this working for a single dynamic sort field. I am stumped how to chain the sorts together dynamically.... – Iannazzi Feb 14 '17 at 02:15
  • thanks again.... I also updated the question to show where I got based on the responses. Based on your update I think I need a better data set and restrictions on sorting, like clicking on id will kill other sorts. I will come back to this when I have that complete! – Iannazzi Feb 14 '17 at 12:36
0

var arr = [{"_row":{},"_parent":{},"id":{"data":112,"cell":{}},"rest": "abcd"},{"_row":{},"_parent":{},"id":{"data":120,"cell":{}},"rest": "rthu"},{"_row":{},"_parent":{},"id":{"data":122,"cell":{}},"rest": "just an example"},{"_row":{},"_parent":{},"id":{"data":124,"cell":{}},"rest": "efgh"}];

arr.sort((a, b) => a.id.data - b.id.data || a.rest.localCompare(b.rest) /* || another comparison ...*/); // switch a and b to swith the order

console.log(arr);
ibrahim mahrir
  • 31,174
  • 5
  • 48
  • 73