如何通过嵌套对象属性对JavaScript对象数组进行排序?

时间:2021-03-03 15:59:25

I have this function to sort a JavaScript array of objects based on a property:

我有这个函数来根据属性对JavaScript对象数组进行排序:

// arr is the array of objects, prop is the property to sort by
var sort = function (prop, arr) {
    arr.sort(function (a, b) {
        if (a[prop] < b[prop]) {
            return -1;
        } else if (a[prop] > b[prop]) {
            return 1;
        } else {
            return 0;
        }
    });
};

It works with arrays like this:

它适用于这样的数组:

sort('property', [
    {property:'1'},
    {property:'3'},
    {property:'2'},
    {property:'4'},
]);

But I want to be able to sort also by nested properties, for example something like:

但我希望能够通过嵌套属性进行排序,例如:

sort('nestedobj.property', [
    {nestedobj:{property:'1'}},
    {nestedobj:{property:'3'}},
    {nestedobj:{property:'2'}},
    {nestedobj:{property:'4'}}
]);

However this doesn't work because it is not possible to do something like object['nestedobj.property'], it should be object['nestedobj']['property'].

但是这不起作用,因为它不可能像object ['nestedobj.property']那样做,它应该是object ['nestedobj'] ['property']。

Do you know how could I solve this problem and make my function work with properties of nested objects?

你知道我怎么能解决这个问题并使我的函数与嵌套对象的属性一起工作?

Thanks in advance

提前致谢

7 个解决方案

#1


14  

You can split the prop on ., and iterate over the Array updating the a and b with the next nested property during each iteration.

您可以在。上拆分prop,并迭代Array,在每次迭代期间使用下一个嵌套属性更新a和b。

Example: http://jsfiddle.net/x8KD6/1/

示例:http://jsfiddle.net/x8KD6/1/

var sort = function (prop, arr) {
    prop = prop.split('.');
    var len = prop.length;

    arr.sort(function (a, b) {
        var i = 0;
        while( i < len ) { a = a[prop[i]]; b = b[prop[i]]; i++; }
        if (a < b) {
            return -1;
        } else if (a > b) {
            return 1;
        } else {
            return 0;
        }
    });
    return arr;
};

#2


5  

Instead of passing the property as a string, pass a function that can retrieve the property from the top level object.

不是将属性作为字符串传递,而是传递一个可以从*对象检索属性的函数。

var sort = function (propertyRetriever, arr) {
    arr.sort(function (a, b) {
        var valueA = propertyRetriever(a);
        var valueB = propertyRetriever(b);

        if (valueA < valueB) {
            return -1;
        } else if (valueA > valueB) {
            return 1;
        } else {
            return 0;
        }
    });
};

Invoke as,

调用为,

var simplePropertyRetriever = function(obj) {
    return obj.property;
};

sort(simplePropertyRetriever, { .. });

Or using a nested object,

或者使用嵌套对象,

var nestedPropertyRetriever = function(obj) {
    return obj.nestedObj.property;
};

sort(nestedPropertyRetriever, { .. });

#3


2  

You can use Agile.js for this kind of things.
Actually you pass an expression instead of callback, it's handle nested properties and javascript expression in a very nice-ish way.

你可以使用Agile.js来做这类事情。实际上你传递的是一个表达而不是回调,它以非常好的方式处理嵌套属性和javascript表达式。

Usage: _.orderBy(array, expression/callback, reverse[optional])

用法:_. orderBy(数组,表达式/回调,反向[可选])

Example:

例:

var orders = [
  { product: { price: 91.12, id: 1 }, date: new Date('01/01/2014') },
  { product: { price: 79.21, id: 2 }, date: new Date('01/01/2014') },
  { product: { price: 99.90, id: 3 }, date: new Date('01/01/2013') },
  { product: { price: 19.99, id: 4 }, date: new Date('01/01/1970') }
];

_.orderBy(orders, 'product.price');
// →  [orders[3], orders[1], orders[0], orders[2]]

_.orderBy(orders, '-product.price');
// → [orders[2], orders[0], orders[1], orders[3]]

#4


1  

Use Array.prototype.sort() with a custom compare function to do the descending sort first:

将Array.prototype.sort()与自定义比较函数一起使用,首先执行降序排序:

champions.sort(function(a, b) { return b.level - a.level }).slice(...

Even nicer with ES6:

ES6甚至更好:

champions.sort((a, b) => b.level - a.level).slice(...

#5


0  

Would this meet your needs?

这会满足您的需求吗?

// arr is the array of objects, prop is the property to sort by
var sort = function (nestedObj, prop, arr) {
    arr.sort(function (a, b) {
        if (a[nestedObj][prop] < b[nestedObj][prop]) {
            return -1;
        } else if (a[nestedObj][prop] > b[nestedObj][prop]) {
            return 1;
        } else {
            return 0;
        }
    });
};

#6


0  

Try this (used a recursive function to get nested value, you can pass the nested property as nestedobj.property): You can use this for any level of hierarchy

试试这个(使用递归函数来获取嵌套值,你可以将嵌套属性作为nestedobj.property传递):你可以将它用于任何级别的层次结构

// arr is the array of objects, prop is the property to sort by
var getProperty = function(obj, propNested){
 if(!obj || !propNested){
  return null;
 }
 else if(propNested.length == 1) {
    var key = propNested[0];
    return obj[key];
 }
 else {
  var newObj = propNested.shift();
    return getProperty(obj[newObj], propNested);
 }
};
var sort = function (prop, arr) {
    arr.sort(function (a, b) {
                var aProp = getProperty(a, prop.split("."));
                var bProp = getProperty(a, prop.split("."));
        if (aProp < bProp) {
            return -1;
        } else if (aProp > bProp) {
            return 1;
        } else {
            return 0;
        }
    });
};

#7


0  

This is my modify code.

这是我的修改代码。

// arr is the array of objects, prop is the property to sort by
var s = function (prop, arr) {
    // add sub function for get value from obj (1/2)
    var _getVal = function(o, key){
        var v = o;
        var k = key.split(".");
        for(var i in k){
            v = v[k[i]];
        }
        return v;
    }
    return arr.sort(function (a, b) {
        // get value from obj a, b before sort (2/2)
        var aVal = _getVal(a, prop);
        var bVal = _getVal(b, prop);
        if (aVal < bVal) {
            return -1;
        } else if (aVal > bVal) {
            return 1;
        } else {
            return 0;
        }
    });
};

#1


14  

You can split the prop on ., and iterate over the Array updating the a and b with the next nested property during each iteration.

您可以在。上拆分prop,并迭代Array,在每次迭代期间使用下一个嵌套属性更新a和b。

Example: http://jsfiddle.net/x8KD6/1/

示例:http://jsfiddle.net/x8KD6/1/

var sort = function (prop, arr) {
    prop = prop.split('.');
    var len = prop.length;

    arr.sort(function (a, b) {
        var i = 0;
        while( i < len ) { a = a[prop[i]]; b = b[prop[i]]; i++; }
        if (a < b) {
            return -1;
        } else if (a > b) {
            return 1;
        } else {
            return 0;
        }
    });
    return arr;
};

#2


5  

Instead of passing the property as a string, pass a function that can retrieve the property from the top level object.

不是将属性作为字符串传递,而是传递一个可以从*对象检索属性的函数。

var sort = function (propertyRetriever, arr) {
    arr.sort(function (a, b) {
        var valueA = propertyRetriever(a);
        var valueB = propertyRetriever(b);

        if (valueA < valueB) {
            return -1;
        } else if (valueA > valueB) {
            return 1;
        } else {
            return 0;
        }
    });
};

Invoke as,

调用为,

var simplePropertyRetriever = function(obj) {
    return obj.property;
};

sort(simplePropertyRetriever, { .. });

Or using a nested object,

或者使用嵌套对象,

var nestedPropertyRetriever = function(obj) {
    return obj.nestedObj.property;
};

sort(nestedPropertyRetriever, { .. });

#3


2  

You can use Agile.js for this kind of things.
Actually you pass an expression instead of callback, it's handle nested properties and javascript expression in a very nice-ish way.

你可以使用Agile.js来做这类事情。实际上你传递的是一个表达而不是回调,它以非常好的方式处理嵌套属性和javascript表达式。

Usage: _.orderBy(array, expression/callback, reverse[optional])

用法:_. orderBy(数组,表达式/回调,反向[可选])

Example:

例:

var orders = [
  { product: { price: 91.12, id: 1 }, date: new Date('01/01/2014') },
  { product: { price: 79.21, id: 2 }, date: new Date('01/01/2014') },
  { product: { price: 99.90, id: 3 }, date: new Date('01/01/2013') },
  { product: { price: 19.99, id: 4 }, date: new Date('01/01/1970') }
];

_.orderBy(orders, 'product.price');
// →  [orders[3], orders[1], orders[0], orders[2]]

_.orderBy(orders, '-product.price');
// → [orders[2], orders[0], orders[1], orders[3]]

#4


1  

Use Array.prototype.sort() with a custom compare function to do the descending sort first:

将Array.prototype.sort()与自定义比较函数一起使用,首先执行降序排序:

champions.sort(function(a, b) { return b.level - a.level }).slice(...

Even nicer with ES6:

ES6甚至更好:

champions.sort((a, b) => b.level - a.level).slice(...

#5


0  

Would this meet your needs?

这会满足您的需求吗?

// arr is the array of objects, prop is the property to sort by
var sort = function (nestedObj, prop, arr) {
    arr.sort(function (a, b) {
        if (a[nestedObj][prop] < b[nestedObj][prop]) {
            return -1;
        } else if (a[nestedObj][prop] > b[nestedObj][prop]) {
            return 1;
        } else {
            return 0;
        }
    });
};

#6


0  

Try this (used a recursive function to get nested value, you can pass the nested property as nestedobj.property): You can use this for any level of hierarchy

试试这个(使用递归函数来获取嵌套值,你可以将嵌套属性作为nestedobj.property传递):你可以将它用于任何级别的层次结构

// arr is the array of objects, prop is the property to sort by
var getProperty = function(obj, propNested){
 if(!obj || !propNested){
  return null;
 }
 else if(propNested.length == 1) {
    var key = propNested[0];
    return obj[key];
 }
 else {
  var newObj = propNested.shift();
    return getProperty(obj[newObj], propNested);
 }
};
var sort = function (prop, arr) {
    arr.sort(function (a, b) {
                var aProp = getProperty(a, prop.split("."));
                var bProp = getProperty(a, prop.split("."));
        if (aProp < bProp) {
            return -1;
        } else if (aProp > bProp) {
            return 1;
        } else {
            return 0;
        }
    });
};

#7


0  

This is my modify code.

这是我的修改代码。

// arr is the array of objects, prop is the property to sort by
var s = function (prop, arr) {
    // add sub function for get value from obj (1/2)
    var _getVal = function(o, key){
        var v = o;
        var k = key.split(".");
        for(var i in k){
            v = v[k[i]];
        }
        return v;
    }
    return arr.sort(function (a, b) {
        // get value from obj a, b before sort (2/2)
        var aVal = _getVal(a, prop);
        var bVal = _getVal(b, prop);
        if (aVal < bVal) {
            return -1;
        } else if (aVal > bVal) {
            return 1;
        } else {
            return 0;
        }
    });
};