【问题标题】:How to sort a JavaScript array of objects by nested object property?如何通过嵌套对象属性对 JavaScript 对象数组进行排序?
【发布时间】:2011-07-01 17:19:43
【问题描述】:

我有这个函数可以根据属性对 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;
        }
    });
};

它适用于这样的数组:

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

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

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

但是这行不通,因为不可能做类似object['nestedobj.property'] 的事情,它应该是object['nestedobj']['property']

您知道如何解决这个问题并让我的函数使用嵌套对象的属性吗?

提前致谢

【问题讨论】:

    标签: javascript arrays sorting


    【解决方案1】:

    这能满足您的需求吗?

    // 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;
            }
        });
    };
    

    【讨论】:

      【解决方案2】:

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

      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;
              }
          });
      };
      

      调用为,

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

      或者使用嵌套对象,

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

      【讨论】:

        【解决方案3】:

        您可以在. 上拆分prop,并迭代数组,在每次迭代期间使用下一个嵌套属性更新ab

        示例: 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;
        };
        

        【讨论】:

        • 感谢这个工作,并且比建议的其他选项更快。
        • @VerizonW:很高兴它成功了。是的,我喜欢尽可能避免函数调用。让事情变得活泼。 :o)
        • 这似乎不适用于不同长度的属性值。例如。 {nestedobj:{property:'g'}}, {nestedobj:{property:'F'}}, {nestedobj:{property:'abcd'}}, {nestedobj:{property:'abba'}}
        【解决方案4】:

        试试这个(使用递归函数来获取嵌套值,您可以将嵌套属性作为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;
                }
            });
        };
        

        【讨论】:

          【解决方案5】:

          这是我的修改代码。

          // 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;
                  }
              });
          };
          

          【讨论】:

            【解决方案6】:

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

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

            示例:

            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]]
            

            【讨论】:

              【解决方案7】:

              使用Array.prototype.sort() 和自定义比较函数先进行降序排序:

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

              使用 ES6 更好:

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

              【讨论】:

                【解决方案8】:

                如果你有类似的对象数组

                const objs = [{
                        first_nom: 'Lazslo',
                        last_nom: 'Jamf',
                        moreDetails: {
                            age: 20
                        }
                    }, {
                        first_nom: 'Pig',
                        last_nom: 'Bodine',
                        moreDetails: {
                            age: 21
                        }
                    }, {
                        first_nom: 'Pirate',
                        last_nom: 'Prentice',
                        moreDetails: {
                            age: 22
                        }
                    }];
                

                你可以简单地使用

                nestedSort = (prop1, prop2 = null, direction = 'asc') => (e1, e2) => {
                        const a = prop2 ? e1[prop1][prop2] : e1[prop1],
                            b = prop2 ? e2[prop1][prop2] : e2[prop1],
                            sortOrder = direction === "asc" ? 1 : -1
                        return (a < b) ? -sortOrder : (a > b) ? sortOrder : 0;
                    }
                

                然后调用它

                对于直接对象

                objs.sort(nestedSort("last_nom"));
                objs.sort(nestedSort("last_nom", null, "desc"));
                

                对于嵌套对象

                objs.sort(nestedSort("moreDetails", "age"));
                objs.sort(nestedSort("moreDetails", "age", "desc"));
                

                【讨论】:

                  【解决方案9】:

                  var objectsArr = [
                    {nestedobj:{property:'1'}},
                    {nestedobj:{property:'3'}},
                    {nestedobj:{property:'2'}},
                    {nestedobj:{property:'4'}}
                  ];
                  
                  function getFromPath(obj, path) {
                    let r = obj;
                    path.forEach(key => { r = r[key]})
                    return r
                  }
                  
                  function sortObjectsArr(objectsArray, ...path) {
                    objectsArray.sort((a, b) => getFromPath(a, path) - getFromPath(b, path))
                  }
                  
                  sortObjectsArr(objectsArr, 'nestedobj', 'property');
                  
                  console.log(objectsArr);

                  不幸的是,我没有找到任何好的方法来使用参数来访问嵌套对象的属性。
                  想提一下,如果键在传递的对象中可用,可以进行一些检查,但这取决于谁以及如何实现。

                  【讨论】:

                    猜你喜欢
                    • 2021-09-19
                    • 2022-11-03
                    • 2012-04-04
                    • 1970-01-01
                    • 2018-02-08
                    • 1970-01-01
                    • 2018-07-02
                    相关资源
                    最近更新 更多