问题描述
我想根据 rank
值在每个元素上添加一个 totalpremium
键(但我不想更改数组内对象的顺序),我的当前代码工作正常,但它改变了我不想做的主对象数组的顺序。
对象数组:
[
{
totalcount: 2834682,totalpremium: 652553820
},{
totalcount: 1380674,totalpremium: 430713235
},{
totalcount: 862119,totalpremium: 165983898
}
]
工作职能
// add rank to each object
function rank_by_key(array,key_name) {
if (key_name) {
array.sort(function (a,b) {
return b[key_name] - a[key_name];
});
var rank = 1;
for (var i = 0; i < array.length; i++) {
if (i > 0 && array[i][key_name] < array[i - 1][key_name]) {
rank++;
}
array[i].rank = rank;
}
}
return array;
}
ranked_result = rank_by_key(array,'totalpremium');
预期输出:
[
{
"totalcount": 2834682,"totalpremium": 110,"rank": 2
},{
"totalcount": 1380674,"totalpremium": 121,"rank":1
},{
"totalcount": 862119,"totalpremium": 100,"rank": 3
}
]
电流输出:
[
{
"totalcount": 1380674,{
"totalcount": 2834682,"rank": 3
}
]
解决方法
这会创建一个 ranks 数组,然后我们可以在创建新属性时引用它
let data = [
{
totalcount: 2834682,totalpremium: 652553820
},{
totalcount: 1380674,totalpremium: 43071324534435
},{
totalcount: 862119,totalpremium: 165983898
}
]
let ranks = data.map(e => e.totalpremium).sort((a,b) => b-a)
let ranked = data.map( e=> ({...e,rank: (ranks.indexOf(e.totalpremium)+1)}));
console.log(ranked)
您只需要一个 sort 字段,别无他物。以下数组索引是您的预期等级。
您可以按如下方式进行操作(如果您需要在元素中显式包含 rank
):
代码示例:
const data = [{totalcount:2834682,totalpremium:652553820},{totalcount:862119,totalpremium:165983898},{totalcount:1380674,totalpremium:430713235}];
const res = [...data].sort((a,b) => b.totalpremium - a.totalpremium)
.map((e,i) => ({rank: i + 1,...e}))
console.log("Result: ",res)
console.log("Original data: ",data)
.as-console-wrapper { max-height: 100% !important; top: 0; } /* ignore this */
可以先对对象进行排序,循环遍历结果,得到对应的对象,并将索引赋值给rank
属性:
Object.entries(arr[0]).sort(([,a],[,b]) => {
return b.totalpremium - a.totalpremium
}).forEach((e,i) => arr[0][(e[0])].rank = i + 1);
结果:
const arr = [{
"A": {
"totalcount": 2834682,"totalpremium": 110
},"B": {
"totalcount": 1380674,"totalpremium": 121
},"C": {
"totalcount": 862119,"totalpremium": 100
}
}]
Object.entries(arr[0]).sort(([,b])=>{return b.totalpremium-a.totalpremium}).forEach((e,i) => arr[0][(e[0])].rank = i+1);
console.log(arr);
从您提供的代码来看,您似乎希望排名考虑平等,例如[10,20,30,20]
将排在 [3,2,1,2]
而不是 [4,3]
。
性能瓶颈将是键的排序,但我们对此无能为力。
这是我得到的,使用了很好的旧函数和中间变量。 一些 ES6 语法糖是有代价的,而集合似乎很有魅力。当老式方法似乎提供了良好的可读性/性能权衡时,我又回到了老式方法,但这当然是很有争议的。
function rank_according_to (data,key_name)
{
// collect unique key values
let unique_keys = new Set();
for (let i = 0 ; i != data.length ; i++) unique_keys.add(data[i][key_name]);
// sort unique key values
let sorted_keys = [...unique_keys].sort( (a,b) => b-a );
// associate a rank to each value
let rank_of_key = [];
for (let i = 0 ; i != sorted_keys.length ; i++) rank_of_key[sorted_keys[i]] = i+1;
// Option 1: Put the ranks into the original array
//for (let i = 0 ; i != data.length ; i++) data[i].rank = rank_of_key[data[i][key_name]];
// Option 2: put the ranks into a deep copy
return data.map (record => ({ ...record,rank: (rank_of_key[record[key_name]]) }) );
}
我更改了您的数据集,使其具有更易读的数字和具有相同键值的情况:
console.log (sample)
Array(4) [ {…},{…},{…} ]
0: Object { key_one: 123,key_two: 121 }
1: Object { key_one: 135,key_two: 110 }
2: Object { key_one: 123,key_two: 151 }
3: Object { key_one: 100,key_two: 100 }
console.log (rank_according_to (sample,"key_one"));
0: Object { key_one: 123,key_two: 121,rank: 2 }
1: Object { key_one: 135,key_two: 110,rank: 1 }
2: Object { key_one: 123,key_two: 151,rank: 2 }
3: Object { key_one: 100,key_two: 100,rank: 3 }
console.log (rank_according_to (sample,"key_two"));
0: Object { key_one: 123,rank: 2 }
1: Object { key_one: 135,rank: 3 }
2: Object { key_one: 123,rank: 1 }
3: Object { key_one: 100,rank: 4 }
现在如果你问我,假设效率不是问题,你可以通过将原始索引保存在“original_order”属性中来省去一些麻烦,这样你就可以在你完成后将数组排序回原位完成生成排名。
无论如何,我对依赖浅拷贝副作用的设计不太满意。它令人困惑,更难维护,并不比许多其他可能的解决方案更有效,而且可能比从一开始就设计更坚固的解决方案的额外成本浪费更多的调试时间。