Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort object-array and additionally a nested array in these objects

Tags:

Updated example, as it didn't match to the object structure which was given in the post

There is an array, which has multiple objects like this:

{ 
    text: text,
    elements: [
        { 
            id: id, 
            page: pageNumber 
        }
    ]
}

Now I need to sort the content in two ways:

  1. Sort all array elements by the text-field

I would do like this:

array.sort(function(a,b) {
    if (a.text < b.text) return -1;
    if (a.text > b.text) return 1;
    return 0;
});
  1. The content of the nested elements-array should also be sorted by the page-field.

I don't know how to sort an array, which is nested in the objects... Unfortunately it gets more difficult as the elements are strings, but represent some page numbers. So how can I sort these data in a correct way, as elements could look like 123 or 23-34? And in the case 123-125, 23-34, the last element should be the first.

Example

[
    {   
        text: 'Some text',
        elements: [
            { id: 1, pages: '45-67' },
            { id: 2, pages: '12-34' }
        ]

    },
    {
        text: 'Another text',
        elements: [
            { id: 3, pages: '12-34' }
        ]
    }
]

Should be sorted to:

[
    {
        text: 'Another text',
        elements: [
            { id: 3, pages: '12-34' }
        ]
    },
    {   
        text: 'Some text',
        elements: [
            { id: 2, pages: '12-34' },
            { id: 1, pages: '45-67' }
        ]

    },
]

So the object order has changed, as A is before S and the order of the page elements in the (now) second object are ordered the other way round.

like image 931
user3142695 Avatar asked Jul 13 '16 12:07

user3142695


2 Answers

For the first part, you could use the power of String#localeCompare. And use for the page array another sort function with splitting the string.

var array = [{ id: 1, text: 'Some text', elements: [{ id: 1, pages: '45-67' }, { id: 2, pages: '12-34' }, { id: 4, pages: '12-5' }, { id: 5, pages: '12' }] }, { id: 3, text: 'Another text', elements: [{ id: 3, pages: '12-34' }] }];

array.sort(function (a, b) {
    return a.text.localeCompare(b.text);
});

array.forEach(function (el) {
    el.elements.sort(function (a, b) {
        var aa = a.pages.split('-'),
            bb = b.pages.split('-');
        return aa[0] - bb[0] || (aa[1] || 0) - (bb[1] || 0);
    });
});
    
console.log(array);
like image 140
Nina Scholz Avatar answered Sep 28 '22 04:09

Nina Scholz


The problem is not a difficult one, as the author has already broken down the problem into smaller chunks. For instance - you mentioned sort the text first and then the nested property (pages), which you are having trouble with.

Your use case is easier as we know you are dealing with pages, so the page numbers will be consistent. E.g. It is unlikely to have entry like '10-20' and '10-15', well if they do then my code below will have to expand a bit to handle this sort of situation.

Otherwise what I have done below is to split the string entry using the - character as delimeter to get a fromPage and toPage parameter. Then using the fromPage we do the usual compare in the sort function to determine who is smaller.

Once that is sorted, then I sort the outer items which you already got a solution for it.

Solution:

// Sort the nested properties first - in this instance it will be page
example.forEach(item => {
  item.pages.sort(function (a, b) {
    aFromPage = a.split('-')[0];
    bFromPage = b.split('-')[0];
    // Assuming from page number is unique, if it is not unique then the ToPage number can be
    // used for comparison also.
    return (aFromPage < bFromPage) ? -1 : 1;
  })
});

// Handle the outer item
example.sort(function(a,b) {
  if (a.text < b.text) return -1;
  if (a.text > b.text) return 1;
  return 0;
});

console.log(JSON.stringify(example));

Output:

[
  {
    "id" : 3,
    "text" : "Another text",
    "pages":
        [
          "1-11",
          "12-34",
          "35-100"
        ]
  },
  {
    "id" : 1,
    "text" : "Some text",
    "pages":
        [
          "12-34",
          "35-44",
          "45-67",
          "68-100"
        ]
  }
]
like image 24
Samuel Toh Avatar answered Sep 28 '22 03:09

Samuel Toh