使用现有函数按特定顺序对JavaScript对象数组进行排序

49
给定一个对象数组:
{
    key: "a",
    value: 42
},
{
    key: "d",
    value: 28
},
{
    key: "c",
    value: 92
},
{
    key: "b",
    value: 87
}
以及一个键的数组:
["c", "a", "b", "d"]
是否有一个 ECMAScript 函数或第三方 JavaScript 库可以让您以一行/函数调用的方式对第一个对象数组进行排序,以匹配在第二个数组中指定的键的顺序,从而使结果为:
{
    key: "c",
    value: 92
},
{
    key: "a",
    value: 42
},
{
    key: "b",
    value: 87
},
{
    key: "d",
    value: 28
}
其他提供函数或算法的问题: 类似/相关的问题:

你尝试过使用array.sort(function())和插入排序/选择排序的组合吗?还是你需要一个第三方解决方案? - neoeahit
我在发布这个问题后找到了一些其他带有解决方案的问题,并更新了问题。因此,现在的目标是找到一个可以直接提供它的东西,而不是重新实现它。 - mkopala
这是一个购物问题,将被关闭。是否有ECMAScript函数或第三方JavaScript库可以让您... - Evan Davis
我对这个问题为什么被投票关闭感到困惑。 - jessegavin
如果有本地化的方法,其他问题中的某个人肯定会提到它。 - lincolnk
6个回答

71

只需使用 indexOf 将键转换为正确的顺序:

var order = ["c", "a", "b", "d"];
_.sortBy(arr, function(obj){ 
    return _.indexOf(order, obj.key);
});

Fiddle

如果有很多键,那么将数组转换为哈希映射表会更加优越,例如:

var order = ["c", "a", "b", "d"];
var orderMap = {};
_.each(order, function(i) { orderMap[i] = _.indexOf(order, i); });

这使得关键字排序查找时间恒定,而不是O(n)。 (Fiddle)


太棒了,回答如此简单。 - jessegavin
1
我喜欢这个。如果能在现有库中找到一个单一的函数,那就更好了。我正在使用CoffeeScript,所以这实际上是一个不错的一行代码:arr = _.sortBy arr, (obj) -> _.indexOf order, obj.key - mkopala
2
@mkopala你可以使用_.mixin()将自己的函数添加到Underscore中。http://underscorejs.org/#mixin - jessegavin
3
这个例子对我来说不是很清晰。这里有一个额外的例子,帮助我理解了它,http://jsfiddle.net/vsn32xp3/。 - Gajus
这里有另一个版本,它支持将未包含在order数组中的数组项排列到已排序数组的后面进行重新排序。在您的版本中,未包含在order数组中的项被放置在已排序数组的前面。 - Raptor
orderMap 也可以直接使用 _.invert(order) 创建。 - Julian

37

迄今为止,已经提供了很好的答案。认为以下也可能是纯JS的替代解决方案:

var arr = arr.sort(function(a,b) {
    return order.indexOf( a.key ) - order.indexOf( b.key );
    //for the sake of recent versions of Google Chrome use:
    //return a.key.charCodeAt(0) > b.key.charCodeAt(0); or return a.key.charCodeAt(0) - b.key.charCodeAt(0);
});

var arr = [
    {
        key: "a",
        value: 42
    },
    {
        key: "d",
        value: 28
    },
    {
        key: "c",
        value: 92
    },
    {
        key: "b",
        value: 87
    }
];

var order = ["c", "a", "b", "d"];

console.log( 'Original: ', JSON.stringify( arr ) );

var arr = arr.sort(function(a,b) {
      return order.indexOf( a.key ) - order.indexOf( b.key );
});

console.log( 'Ordered: ', JSON.stringify( arr ) );


5
在 Chrome 60 中,我无法直接从排序回调函数中返回布尔值,相反需要返回 -101 中的一个。 - Bardi Harborow
@BardiHarborow,return a.key.charCodeAt(0) > b.key.charCodeAt(0); 或者 return a.key.charCodeAt(0) - b.key.charCodeAt(0); 怎么样? - PeterKA
9
我非常确定这应该是 order.indexOf(a.key) - order.indexOf(b.key) 而不是一个布尔比较运算符。 - Gajus
1
非常好的答案,谢谢。 - Maor Barazani
我假设你的 order 变量指的是 var order = ["c", "a", "b", "d"]; - Danny Bullis

18

const obj = [
    {
        key: "a",
        value: 42
    },
    {
        key: "d",
        value: 28
    },
    {
        key: "c",
        value: 92
    },
    {
        key: "b",
        value: 87
    }
]


const sortList = ["c", "a", "b", "d"];
    
    
const sortedObj = obj.sort((a, b) => {
    return (
        sortList.indexOf(a.key) - sortList.indexOf(b.key)
    );
});

console.log(sortedObj );


3

我不能保证这是最高效的方法,但你可以使用每个对象的key作为另一个对象属性的键。然后通过这些键简单地访问它们。

for (x = 0; x < objn.length; x++) {
    newobj[objn[x].key] = objn[x];
}
objn = [];
for (x = 0; x < keys.length; x++) {
    objn.push(newobj[keys[x]]);
}
console.log(objn);

http://jsfiddle.net/WdehF/


2
const data = [{key:"a"},{key:"d"},{key:"c"},{key:"b"}]      // <-your data
const order = ["c", "a", "b", "d"]          // <-create an array in the order you wish
const orderedArray = order.map(char=>data.find(res=>res.key===char))     // <- what you want

对于每个在order里的char:如果这个char等于你的data中任何一个key,就会进行map操作并且连续返回。


1
// create hash map el.key -> index, to help us with direct access, avoid searching
const hashMap = arr.reduce((acc, el, index) => { acc[el.id] = el; return acc }, {})

// finally, map the ids to the final result
const ids.map(id => hashMap[id])

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