[英]How to sort a JavaScript array of objects by nested object property?
我有这个 function 来根据属性对 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;
}
});
};
它像这样与 arrays 一起工作:
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']
。
你知道我如何解决这个问题并让我的 function 使用嵌套对象的属性吗?
提前致谢
您prop
在.
, 并在每次迭代期间使用下一个嵌套属性更新a
和b
的数组。
示例: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;
};
不要将属性作为字符串传递,而是传递一个可以从顶级对象检索属性的函数。
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, { .. });
使用Array.prototype.sort()
和自定义比较函数首先进行降序排序:
champions.sort(function(a, b) { return b.level - a.level }).slice(...
使用 ES6 更好:
champions.sort((a, b) => b.level - a.level).slice(...
如果你有像这样的对象数组
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"));
您可以将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]]
这能满足您的需求吗?
// 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;
}
});
};
试试这个(使用递归函数来获取嵌套值,您可以将嵌套属性作为 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;
}
});
};
这是我的修改代码。
// 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;
}
});
};
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);
不幸的是,我没有找到任何好的方法来使用参数来访问嵌套对象的属性。
想提一下,如果键在传递的对象中可用,可以进行一些检查,但这取决于谁以及如何实现这一点。
我的解决方案是这个。 我决定先将物体展平:
function flattenObject(value: any): any {
let toReturn: any = {};
for (const i in value) {
if (!value.hasOwnProperty(i)) {
continue;
}
if (typeof value[i] == 'object') {
const flatObject = flattenObject(value[i]);
for (const x in flatObject) {
if (!flatObject.hasOwnProperty(x)) continue;
toReturn[i + '.' + x] = flatObject[x];
}
} else {
toReturn[i] = value[i];
}
}
return toReturn;
}
然后我将从对象中提取值:
function nestedFieldValue(
nestedJoinedFieldByDot: string,
obj: any,
): any {
return flattenObject(obj)[nestedJoinedFieldByDot];
}
蚂蚁最后我只需要这样做:
export function fieldSorter(fields: string[]) {
return function (a: any, b: any) {
return fields
.map(function (fieldKey) {
// README: Sort Ascending by default
let dir = 1;
if (fieldKey[0] === '-') {
// README: Sort Descending if `-` was passed at the beginning of the field name
dir = -1;
fieldKey = fieldKey.substring(1);
}
const aValue = nestedFlattenObjectFieldValue(
fieldKey,
a,
);
const bValue = nestedFlattenObjectFieldValue(
fieldKey,
b,
);
if (
typeof aValue === 'number' ||
typeof bValue === 'number'
) {
/**
* README: default value when the field does not exists to prevent unsorted array
* I assume that 0 should be the last element. In other word I sort arrays in a way
* that biggest numbers comes first and then smallest numbers
*/
if (aValue ?? 0 > bValue ?? 0) {
return dir;
}
if (aValue ?? 0 < bValue ?? 0) {
return -dir;
}
} else {
if (aValue ?? 0 > bValue ?? 0) {
return dir;
}
if (aValue ?? 0 < bValue ?? 0) {
return -dir;
}
}
return 0;
})
.reduce(function firstNonZeroValue(p, n) {
return p ? p : n;
}, 0);
};
}
最后我们需要这样做:
const unsorted = [
{
city: {
priority: 1,
name: 'Tokyo',
airport: { name: 'Haneda Airport' }
}
}
]
const result = unsorted.sort(
fieldSorter(['city.priority', 'city.airport.name', 'city.name']),
);
我认为这种方式要清晰得多。 它具有可读性和功能性。 我合并了来自stackoverflow的多个答案以达到这个解决方案:sweat_smile:
这应该可以工作并且可以接受多个参数。
https://codepen.io/allenACR/pen/VwQKWZG
function sortArrayOfObjects(items, getter) {
const copy = JSON.parse(JSON.stringify(items));
const sortFn = fn => {
copy.sort((a, b) => {
a = fn(a)
b = fn(b)
return a === b ? 0 : a < b ? -1 : 1;
});
};
getter.forEach(x => {
const fn = typeof x === 'function' ? x : item => item[x];
sortFn(fn);
});
return copy;
}
// example dataset
const data = [
{id: 3, name: "Dave", details: {skill: "leader"} },
{id: 1, name: "Razor", details: {skill: "music"} },
{id: 2, name: "Syd", details: {skill: "animal husbandry"} }
]
// sort via single prop
const sort1 = sortArrayOfObjects(data, ["id"])
// returns [Razor, Syd, Dave]
// sort via nested
const sort2 = sortArrayOfObjects(data, [
(item) => item.details.skill
])
// returns [Syd, Dave, Razor]
console.log({sort1, sort2})
3层深。 路径可能看起来像这样。 'level1' 或 'level1.level2' 或 'level1.level2.level3' 我也做了大写的排序我的所有项目都是字符串。 Anwser 是来自 - @Mas 的修改版本
public keysrt(arr: Object[], path: string, reverse: boolean): void {
const nextOrder = reverse ? -1 : 1;
const pathSplit = path.split('.');
if (arr === null || arr === undefined ) {
return;
}
if (arr.length <= 1) {
return;
}
const nestedSort = (prop1, prop2 = null, prop3 = null, direction = 'asc') => (e1, e2) => {
const a = prop3 ? e1[prop1][prop2][prop3] : prop2 ? e1[prop1][prop2] : e1[prop1],
b = prop3 ? e2[prop1][prop2][prop3] : prop2 ? e2[prop1][prop2] : e2[prop1],
sortOrder = direction === 'asc' ? 1 : -1;
return (a.toString().toUpperCase() < b.toString().toUpperCase()) ?
-sortOrder * nextOrder : (a.toString().toUpperCase() > b.toString().toUpperCase()) ?
sortOrder * nextOrder : 0;
};
if (pathSplit.length === 3) {
arr.sort(nestedSort(pathSplit[0], pathSplit[1], pathSplit[2]));
}
if (pathSplit.length === 2) {
arr.sort(nestedSort(pathSplit[0], pathSplit[1]));
}
if (pathSplit.length === 1) {
arr.sort(nestedSort(pathSplit[0], null));
}
}
对于那些正在研究如何对嵌套属性进行排序的人。 我创建了一个类型安全的小型数组排序方法,支持深度嵌套属性和 Typescript 自动完成。
https://github.com/jvandenaardweg/sort-by-property
https://www.npmjs.com/package/sort-by-property
例子:
blogPosts.sort(sortByProperty('author.name', 'asc'));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.