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?
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.
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>");
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With