Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

best way to sort list based on key in nested object array in java script

Tags:

javascript

I have the following:

var list = [
    {"item":[{a:5, a1:6, a2:7}, {b:3, b1:4, b2:2}]},
    {"item":[{a:1, a1:2, a2:3}, {b:4, b1:5, b2:6}]},
    {"item":[{a:2, a1:7, a2:4}, {b:3, b1:7, b2:1}]}
];

Assume that I have the variable list above, how can I sort it such that all the immediate objects with the item key in the list are sorted in ascending order based on a key (i.e. "a1" or "b"). Note that it would not change or reorder the list in list[x]["item"], but only the immediate items in list[x].

The standard sort function appears to sort only on keys within objects within arrays, but I want to sort based on a key located in a nested object in an array.

What is the best way to sort this?

like image 301
Rolando Avatar asked Oct 16 '14 23:10

Rolando


2 Answers

So your main issue is that you need to find an object in the internal item array with the matching property. Since you don't know what object it will be located on. Note that a limitation here is that you're always going to be comparing on the first found instance, even if more than one object in item possesses the comparison property. Here goes:

var list = [
    {"item":[{a:5, a1:6, a2:7}, {b:3, b1:4, b2:2}]},
    {"item":[{a:1, a1:2, a2:3}, {b:4, b1:5, b2:6}]},
    {"item":[{a:2, a1:7, a2:4}, {b:3, b1:7, b2:1}]}
];

function comparatorMaker(prop) {
    var findVal = function(acc, obj) {
        return acc || obj[prop];
    };

    return function(x, y) {
        if (!x.item && !y.item) return 0;
        if (x.item && !y.item) return -1;
        if (!x.item && y.item) return 1;

        var xVal = x.item.reduce(findVal, null) || 0;
        var yVal = y.item.reduce(findVal, null) || 0;

        return (xVal === yVal) ? 0 : (xVal > yVal) ? 1 : -1;
    };
}

var myComparator = comparatorMaker('a');

list.sort(myComparator); // element 1, element 2, element 0

What's happening here is that we generate a unique comparator function for a given property name. It will now work with any:

var myComparator = comparatorMaker('b1');

list.sort(myComparator); // element 0, element 1, element 2

The findVal function that we define when making the comparator function is used with item.reduce. The reduction iterates over the contents of item and returns either the already-found value or looks for the value on the current element under examination. This could be done more efficiently, actually, since we end up iterating every element in item even if we find a match immediately, but it would take more lines of code to demonstrate that so I kept it simple.

The comparator itself should return 1, 0 or -1 depending on whether the resulting values are found to be greater-lesser, equal, or lesser-greater. The first few lines of the comparator function are just to handle cases where elements in list do not actually have the item property, since the way you phrased the question, it sounded like this might sometimes be the case.

like image 128
Semicolon Avatar answered Sep 23 '22 01:09

Semicolon


Javascript's built-in sort method will do what you want, you just have to pass it the appropriate function (which is the tricky part). You can do it any number of ways really, but one easy way would be by creating a sortBy function that takes the keys, indexes, etc. that you need to get the actual value you want to sort on and returns a comparator function.

Something like this:

function sortBy(key1, index, key2) {
    return function(a, b) {
        return a[key1][index][key2] - b[key1][index][key2];
    }
}

Which you'd then pass to the sort function like this:

list.sort(sortBy("item", 0, "a1"));

Which would sort your particular data structure by a1.

var list = [
    {"item":[{a:5, a1:6, a2:7}, {b:3, b1:4, b2:2}]},
    {"item":[{a:1, a1:2, a2:3}, {b:4, b1:5, b2:6}]},
    {"item":[{a:2, a1:7, a2:4}, {b:3, b1:7, b2:1}]}
];

function sortBy(key1, index, key2) {
    return function(a, b) {
        return a[key1][index][key2] - b[key1][index][key2];
    }
}

list.sort(sortBy("item", 0, "a1"));

for (idx in list) {
  document.write("<pre>" + JSON.stringify(list[idx]) + "</pre>");
}
like image 22
Mike S Avatar answered Sep 21 '22 01:09

Mike S