개체 배열을 여러 필드로 정렬하려면 어떻게 해야 합니까?
이 첫 번째 질문에서 여러 필드에 정렬을 적용하는 방법은 무엇입니까?
약간 적응된 이 구조를 사용하여 도시(상승)와 가격(하강)을 어떻게 구분합니까?
var homes = [
{"h_id":"3",
"city":"Dallas",
"state":"TX",
"zip":"75201",
"price":"162500"},
{"h_id":"4",
"city":"Bevery Hills",
"state":"CA",
"zip":"90210",
"price":"319250"},
{"h_id":"6",
"city":"Dallas",
"state":"TX",
"zip":"75000",
"price":"556699"},
{"h_id":"5",
"city":"New York",
"state":"NY",
"zip":"00010",
"price":"962500"}
];
나는 일반적인 접근법을 제공하는 답변보다 그 사실이 더 마음에 들었다.이 코드를 사용할 예정이라면 날짜나 다른 것도 분류해야 합니다.그 물체를 "주력"으로 만드는 능력은 조금 번거롭지는 않더라도 편리해 보였다.
나는 이 답을 좋은 일반적인 예로 만들려고 노력했지만, 별로 운이 좋지 않다.
값이 0이 아닐 때까지 값의 델타를 취함으로써 연쇄 정렬 방식을 사용할 수 있습니다.
var data = [{ h_id: "3", city: "Dallas", state: "TX", zip: "75201", price: "162500" }, { h_id: "4", city: "Bevery Hills", state: "CA", zip: "90210", price: "319250" }, { h_id: "6", city: "Dallas", state: "TX", zip: "75000", price: "556699" }, { h_id: "5", city: "New York", state: "NY", zip: "00010", price: "962500" }];
data.sort(function (a, b) {
return a.city.localeCompare(b.city) || b.price - a.price;
});
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
또는 es6를 사용하면 다음과 같이 간단하게 할 수 있습니다.
data.sort((a, b) => a.city.localeCompare(b.city) || b.price - a.price);
문제를 쉽게 해결할 수 있는 간단한 솔루션:
homes.sort(
function(a, b) {
if (a.city === b.city) {
// Price is only important when cities are the same
return b.price - a.price;
}
return a.city > b.city ? 1 : -1;
});
업데이트: 다음은 "최적화된" 버전입니다.이 기능은 훨씬 더 많은 전처리를 수행하며 각 정렬 옵션에 대한 비교 기능을 미리 만듭니다.각 정렬 옵션에 대한 기능을 저장하기 때문에 더 많은 메모리가 필요할 수 있지만, 비교 중에 올바른 설정을 결정할 필요가 없기 때문에 조금 더 잘 준비해야 합니다.난 프로파일링을 한 적이 없어.
var sort_by;
(function() {
// utility functions
var default_cmp = function(a, b) {
if (a == b) return 0;
return a < b ? -1 : 1;
},
getCmpFunc = function(primer, reverse) {
var dfc = default_cmp, // closer in scope
cmp = default_cmp;
if (primer) {
cmp = function(a, b) {
return dfc(primer(a), primer(b));
};
}
if (reverse) {
return function(a, b) {
return -1 * cmp(a, b);
};
}
return cmp;
};
// actual implementation
sort_by = function() {
var fields = [],
n_fields = arguments.length,
field, name, reverse, cmp;
// preprocess sorting options
for (var i = 0; i < n_fields; i++) {
field = arguments[i];
if (typeof field === 'string') {
name = field;
cmp = default_cmp;
}
else {
name = field.name;
cmp = getCmpFunc(field.primer, field.reverse);
}
fields.push({
name: name,
cmp: cmp
});
}
// final comparison function
return function(A, B) {
var a, b, name, result;
for (var i = 0; i < n_fields; i++) {
result = 0;
field = fields[i];
name = field.name;
result = field.cmp(A[name], B[name]);
if (result !== 0) break;
}
return result;
}
}
}());
사용 예:
homes.sort(sort_by('city', {name:'price', primer: parseInt, reverse: true}));
원래 기능:
var sort_by = function() {
var fields = [].slice.call(arguments),
n_fields = fields.length;
return function(A,B) {
var a, b, field, key, primer, reverse, result, i;
for(i = 0; i < n_fields; i++) {
result = 0;
field = fields[i];
key = typeof field === 'string' ? field : field.name;
a = A[key];
b = B[key];
if (typeof field.primer !== 'undefined'){
a = field.primer(a);
b = field.primer(b);
}
reverse = (field.reverse) ? -1 : 1;
if (a<b) result = reverse * -1;
if (a>b) result = reverse * 1;
if(result !== 0) break;
}
return result;
}
};
다음은 단순한 기능적 범용 접근법입니다.배열을 사용하여 정렬 순서를 지정합니다.내림차순을 지정하려면 -를 추가합니다.
var homes = [
{"h_id":"3", "city":"Dallas", "state":"TX","zip":"75201","price":"162500"},
{"h_id":"4","city":"Bevery Hills", "state":"CA", "zip":"90210", "price":"319250"},
{"h_id":"6", "city":"Dallas", "state":"TX", "zip":"75000", "price":"556699"},
{"h_id":"5", "city":"New York", "state":"NY", "zip":"00010", "price":"962500"}
];
homes.sort(fieldSorter(['city', '-price']));
// homes.sort(fieldSorter(['zip', '-state', 'price'])); // alternative
function fieldSorter(fields) {
return function (a, b) {
return fields
.map(function (o) {
var dir = 1;
if (o[0] === '-') {
dir = -1;
o=o.substring(1);
}
if (a[o] > b[o]) return dir;
if (a[o] < b[o]) return -(dir);
return 0;
})
.reduce(function firstNonZeroValue (p,n) {
return p ? p : n;
}, 0);
};
}
편집: ES6에서는 더 짧아!
"use strict";
const fieldSorter = (fields) => (a, b) => fields.map(o => {
let dir = 1;
if (o[0] === '-') { dir = -1; o=o.substring(1); }
return a[o] > b[o] ? dir : a[o] < b[o] ? -(dir) : 0;
}).reduce((p, n) => p ? p : n, 0);
const homes = [{"h_id":"3", "city":"Dallas", "state":"TX","zip":"75201","price":162500}, {"h_id":"4","city":"Bevery Hills", "state":"CA", "zip":"90210", "price":319250},{"h_id":"6", "city":"Dallas", "state":"TX", "zip":"75000", "price":556699},{"h_id":"5", "city":"New York", "state":"NY", "zip":"00010", "price":962500}];
const sortedHomes = homes.sort(fieldSorter(['state', '-price']));
document.write('<pre>' + JSON.stringify(sortedHomes, null, '\t') + '</pre>')
오늘은 범용 멀티 기능 선별기를 만들었습니다.다음 URL에서 By.js를 보실 수 있습니다.https://github.com/Teun/thenBy.js
표준 Array.sort를 사용할 수 있지만 firstBy().thenBy() 스타일과 함께 사용할 수 있습니다.위의 솔루션보다 코드와 복잡성이 훨씬 낮습니다.
다음은 여러 필드별로 정렬할 수 있는 확장 가능한 방법입니다.
homes.sort(function(left, right) {
var city_order = left.city.localeCompare(right.city);
var price_order = parseInt(left.price) - parseInt(right.price);
return city_order || -price_order;
});
메모들
- 배열 정렬에 전달된 함수는 음수, 0, 양의 값을 반환하여 더 작음, 같음, 더 크다는 것을 나타냅니다.
a.localeCompare(b)
문자열에 대해 보편적으로 지원되며 -1,0,1을 반환합니다.a<b
,a==b
,a>b
.- 되는데, 왜냐하면 뺄셈은 숫자 필드에 적용되기 때문입니다.
a - b
, , + - , 0 , + :a<b
,a==b
,a>b
. ||
것은 " " " 입니다.city
over 」price
.- , 합니다( 「」).
-price_order
- 합니다. " or-chain " " 。
return city_order || -price_order || date_order;
- 1970년 이후 날짜 계산이 밀리초로 변환되기 때문에 날짜를 빼기와 비교합니다.
var date_order = new Date(left.date) - new Date(right.date);
- 뺄셈과 부울 비교. true와 false가 각각 1과 0으로 바뀝니다(따라서 뺄셈을 통해 -1 또는 0 또는 1이 생성됩니다).
var goodness_order = Boolean(left.is_good) - Boolean(right.is_good)
이는 부울 생성자가 이미 부울 생성자일지라도 주의를 끌도록 제안할 정도로 이례적입니다.
이것은 완전한 속임수이지만 기본적으로 바로 사용할 수 있는 캔 라이브러리 기능이기 때문에 이 질문에 가치를 더한다고 생각합니다.
로 에 액세스 할 수 lodash
Lodash와 같은 .underscore
하면 '아까운데'를 할 수 요._.sortBy
방법.다음 스니펫은 Lodash 문서에서 직접 복사한 것입니다.
이 예에서 코멘트된 결과는 어레이 어레이를 반환하는 것처럼 보이지만, 이는 개체 어레이인 실제 결과가 아니라 순서만 보여줍니다.
var users = [
{ 'user': 'fred', 'age': 48 },
{ 'user': 'barney', 'age': 36 },
{ 'user': 'fred', 'age': 40 },
{ 'user': 'barney', 'age': 34 }
];
_.sortBy(users, [function(o) { return o.user; }]);
// => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
_.sortBy(users, ['user', 'age']);
// => objects for [['barney', 34], ['barney', 36], ['fred', 40], ['fred', 48]]
다음 함수를 사용하면 각 속성에서 오름차순(기본값) 또는 내림차순 중 하나 또는 여러 속성에서 개체 배열을 정렬할 수 있으며 대소문자를 구분하여 비교할지 여부를 선택할 수 있습니다.기본적으로는 이 함수는 대소문자를 구분하지 않는 정렬을 수행합니다.
첫 번째 인수는 개체를 포함하는 배열이어야 합니다.후속 인수는 정렬할 여러 개체 속성을 참조하는 쉼표로 구분된 문자열 목록이어야 합니다. 인수정렬을 를 구분하는 정렬을 사용합니다.사용true
대소문자를 구분하는 정렬입니다.
이 함수는 기본적으로 각 속성/키를 오름차순으로 정렬합니다.합니다.['property_name', true]
.
서 설명).homes
는 오브젝트를 포함하는 배열입니다).
objSort(homes, 'city')
, 대소문자 없음) --> 도시별 정렬(소문자 구분, 대소문자 구분)
objSort(homes, ['city', true])
대소문자 없음) --> 도시별 정렬(대소문자 구분)
objSort(homes, 'city', true)
--> 도시별로 정렬 후 가격(대소문자 구분)
objSort(homes, 'city', 'price')
시군/군/군군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/군/
objSort(homes, 'city', ['price', true])
별 정렬 후 대소문자 ) --> "(도)", "(도)", "(도(도)", "도(도)", "도(도)"
더 이상 소란을 피우지 않고, 그 기능은 다음과 같습니다.
function objSort() {
var args = arguments,
array = args[0],
case_sensitive, keys_length, key, desc, a, b, i;
if (typeof arguments[arguments.length - 1] === 'boolean') {
case_sensitive = arguments[arguments.length - 1];
keys_length = arguments.length - 1;
} else {
case_sensitive = false;
keys_length = arguments.length;
}
return array.sort(function (obj1, obj2) {
for (i = 1; i < keys_length; i++) {
key = args[i];
if (typeof key !== 'string') {
desc = key[1];
key = key[0];
a = obj1[args[i][0]];
b = obj2[args[i][0]];
} else {
desc = false;
a = obj1[args[i]];
b = obj2[args[i]];
}
if (case_sensitive === false && typeof a === 'string') {
a = a.toLowerCase();
b = b.toLowerCase();
}
if (! desc) {
if (a < b) return -1;
if (a > b) return 1;
} else {
if (a > b) return -1;
if (a < b) return 1;
}
}
return 0;
});
} //end of objSort() function
다음은 샘플 데이터입니다.
var homes = [{
"h_id": "3",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"price": 162500
}, {
"h_id": "4",
"city": "Bevery Hills",
"state": "CA",
"zip": "90210",
"price": 1000000
}, {
"h_id": "5",
"city": "new york",
"state": "NY",
"zip": "00010",
"price": 1000000
}, {
"h_id": "6",
"city": "Dallas",
"state": "TX",
"zip": "85000",
"price": 300000
}, {
"h_id": "7",
"city": "New York",
"state": "NY",
"zip": "00020",
"price": 345000
}];
MULTIPE 키를 사용하여 동적으로 이를 수행할 수 있습니다.
- 정렬의 각 col/key에서 고유한 값 필터링
- 그것을 정돈하거나 되돌리다
- indexOf(값) 키 값을 기준으로 각 개체에 대한 가중치 폭 제로패드 추가
- 불연속 웨이트를 사용하여 정렬하다
Object.defineProperty(Array.prototype, 'orderBy', {
value: function(sorts) {
sorts.map(sort => {
sort.uniques = Array.from(
new Set(this.map(obj => obj[sort.key]))
);
sort.uniques = sort.uniques.sort((a, b) => {
if (typeof a == 'string') {
return sort.inverse ? b.localeCompare(a) : a.localeCompare(b);
}
else if (typeof a == 'number') {
return sort.inverse ? b - a : a - b;
}
else if (typeof a == 'boolean') {
let x = sort.inverse ? (a === b) ? 0 : a? -1 : 1 : (a === b) ? 0 : a? 1 : -1;
return x;
}
return 0;
});
});
const weightOfObject = (obj) => {
let weight = "";
sorts.map(sort => {
let zeropad = `${sort.uniques.length}`.length;
weight += sort.uniques.indexOf(obj[sort.key]).toString().padStart(zeropad, '0');
});
//obj.weight = weight; // if you need to see weights
return weight;
}
this.sort((a, b) => {
return weightOfObject(a).localeCompare( weightOfObject(b) );
});
return this;
}
});
용도:
// works with string, number and boolean
let sortered = your_array.orderBy([
{key: "type", inverse: false},
{key: "title", inverse: false},
{key: "spot", inverse: false},
{key: "internal", inverse: true}
]);
다음은 각 레벨에서 반전 및/또는 매핑이 가능한 일반적인 다차원 정렬입니다.
타이프 스크립트로 작성.Javascript의 경우 이 JSFiddle을 확인하십시오.
코드
type itemMap = (n: any) => any;
interface SortConfig<T> {
key: keyof T;
reverse?: boolean;
map?: itemMap;
}
export function byObjectValues<T extends object>(keys: ((keyof T) | SortConfig<T>)[]): (a: T, b: T) => 0 | 1 | -1 {
return function(a: T, b: T) {
const firstKey: keyof T | SortConfig<T> = keys[0];
const isSimple = typeof firstKey === 'string';
const key: keyof T = isSimple ? (firstKey as keyof T) : (firstKey as SortConfig<T>).key;
const reverse: boolean = isSimple ? false : !!(firstKey as SortConfig<T>).reverse;
const map: itemMap | null = isSimple ? null : (firstKey as SortConfig<T>).map || null;
const valA = map ? map(a[key]) : a[key];
const valB = map ? map(b[key]) : b[key];
if (valA === valB) {
if (keys.length === 1) {
return 0;
}
return byObjectValues<T>(keys.slice(1))(a, b);
}
if (reverse) {
return valA > valB ? -1 : 1;
}
return valA > valB ? 1 : -1;
};
}
사용 예
성, 이름 순으로 사용자 배열 정렬:
interface Person {
firstName: string;
lastName: string;
}
people.sort(byObjectValues<Person>(['lastName','firstName']));
언어 코드를 언어 코드가 아닌 이름으로 정렬합니다( 참조).map
버전을 내림차순합니다( 참조).reverse
를 참조해 주세요.
interface Language {
code: string;
version: number;
}
// languageCodeToName(code) is defined elsewhere in code
languageCodes.sort(byObjectValues<Language>([
{
key: 'code',
map(code:string) => languageCodeToName(code),
},
{
key: 'version',
reverse: true,
}
]));
여기 구문에 대한 당신의 아이디어에 가까운 또 다른 것이 있습니다.
function sortObjects(objArray, properties /*, primers*/) {
var primers = arguments[2] || {}; // primers are optional
properties = properties.map(function(prop) {
if( !(prop instanceof Array) ) {
prop = [prop, 'asc']
}
if( prop[1].toLowerCase() == 'desc' ) {
prop[1] = -1;
} else {
prop[1] = 1;
}
return prop;
});
function valueCmp(x, y) {
return x > y ? 1 : x < y ? -1 : 0;
}
function arrayCmp(a, b) {
var arr1 = [], arr2 = [];
properties.forEach(function(prop) {
var aValue = a[prop[0]],
bValue = b[prop[0]];
if( typeof primers[prop[0]] != 'undefined' ) {
aValue = primers[prop[0]](aValue);
bValue = primers[prop[0]](bValue);
}
arr1.push( prop[1] * valueCmp(aValue, bValue) );
arr2.push( prop[1] * valueCmp(bValue, aValue) );
});
return arr1 < arr2 ? -1 : 1;
}
objArray.sort(function(a, b) {
return arrayCmp(a, b);
});
}
// just for fun use this to reverse the city name when sorting
function demoPrimer(str) {
return str.split('').reverse().join('');
}
// Example
sortObjects(homes, ['city', ['price', 'desc']], {city: demoPrimer});
데모: http://jsfiddle.net/Nq4dk/2/
편집: 재미삼아 SQL과 같은 문자열을 사용하여 다음과 같은 작업을 수행할 수 있습니다.sortObjects(homes, "city, price desc")
function sortObjects(objArray, properties /*, primers*/) {
var primers = arguments[2] || {};
properties = properties.split(/\s*,\s*/).map(function(prop) {
prop = prop.match(/^([^\s]+)(\s*desc)?/i);
if( prop[2] && prop[2].toLowerCase() === 'desc' ) {
return [prop[1] , -1];
} else {
return [prop[1] , 1];
}
});
function valueCmp(x, y) {
return x > y ? 1 : x < y ? -1 : 0;
}
function arrayCmp(a, b) {
var arr1 = [], arr2 = [];
properties.forEach(function(prop) {
var aValue = a[prop[0]],
bValue = b[prop[0]];
if( typeof primers[prop[0]] != 'undefined' ) {
aValue = primers[prop[0]](aValue);
bValue = primers[prop[0]](bValue);
}
arr1.push( prop[1] * valueCmp(aValue, bValue) );
arr2.push( prop[1] * valueCmp(bValue, aValue) );
});
return arr1 < arr2 ? -1 : 1;
}
objArray.sort(function(a, b) {
return arrayCmp(a, b);
});
}
간단한 것:
var someArray = [...];
function generateSortFn(props) {
return function (a, b) {
for (var i = 0; i < props.length; i++) {
var prop = props[i];
var name = prop.name;
var reverse = prop.reverse;
if (a[name] < b[name])
return reverse ? 1 : -1;
if (a[name] > b[name])
return reverse ? -1 : 1;
}
return 0;
};
};
someArray.sort(generateSortFn([{name: 'prop1', reverse: true}, {name: 'prop2'}]));
왜 복잡해?두 번만 분류해!(중요도 순서를 최소에서 최대로 되돌리기만 하면 됩니다).
jj.sort( (a, b) => (a.id >= b.id) ? 1 : -1 );
jj.sort( (a, b) => (a.status >= b.status) ? 1 : -1 );
나는 SnowBurnt의 접근법을 좋아하지만 차이가 아닌 도시에서의 동등성을 테스트하기 위해서는 수정이 필요하다.
homes.sort(
function(a,b){
if (a.city==b.city){
return (b.price-a.price);
} else {
return (a.city-b.city);
}
});
여기 Schwartzian 트랜스포메이션 관용어에 기초한 솔루션이 있습니다. 도움이 되시길 바랍니다.
function sortByAttribute(array, ...attrs) {
// generate an array of predicate-objects contains
// property getter, and descending indicator
let predicates = attrs.map(pred => {
let descending = pred.charAt(0) === '-' ? -1 : 1;
pred = pred.replace(/^-/, '');
return {
getter: o => o[pred],
descend: descending
};
});
// schwartzian transform idiom implementation. aka: "decorate-sort-undecorate"
return array.map(item => {
return {
src: item,
compareValues: predicates.map(predicate => predicate.getter(item))
};
})
.sort((o1, o2) => {
let i = -1, result = 0;
while (++i < predicates.length) {
if (o1.compareValues[i] < o2.compareValues[i]) result = -1;
if (o1.compareValues[i] > o2.compareValues[i]) result = 1;
if (result *= predicates[i].descend) break;
}
return result;
})
.map(item => item.src);
}
다음은 사용 예를 제시하겠습니다.
let games = [
{ name: 'Pako', rating: 4.21 },
{ name: 'Hill Climb Racing', rating: 3.88 },
{ name: 'Angry Birds Space', rating: 3.88 },
{ name: 'Badland', rating: 4.33 }
];
// sort by one attribute
console.log(sortByAttribute(games, 'name'));
// sort by mupltiple attributes
console.log(sortByAttribute(games, '-rating', 'name'));
다른 방법
var homes = [
{"h_id":"3",
"city":"Dallas",
"state":"TX",
"zip":"75201",
"price":"162500"},
{"h_id":"4",
"city":"Bevery Hills",
"state":"CA",
"zip":"90210",
"price":"319250"},
{"h_id":"6",
"city":"Dallas",
"state":"TX",
"zip":"75000",
"price":"556699"},
{"h_id":"5",
"city":"New York",
"state":"NY",
"zip":"00010",
"price":"962500"}
];
function sortBy(ar) {
return ar.sort((a, b) => a.city === b.city ?
b.price.toString().localeCompare(a.price) :
a.city.toString().localeCompare(b.city));
}
console.log(sortBy(homes));
그냥 다른 선택지일 뿐이야다음의 유틸리티 기능을 사용하는 것을 고려해 주세요.
/** Performs comparing of two items by specified properties
* @param {Array} props for sorting ['name'], ['value', 'city'], ['-date']
* to set descending order on object property just add '-' at the begining of property
*/
export const compareBy = (...props) => (a, b) => {
for (let i = 0; i < props.length; i++) {
const ascValue = props[i].startsWith('-') ? -1 : 1;
const prop = props[i].startsWith('-') ? props[i].substr(1) : props[i];
if (a[prop] !== b[prop]) {
return a[prop] > b[prop] ? ascValue : -ascValue;
}
}
return 0;
};
사용 예(고객의 경우):
homes.sort(compareBy('city', '-price'));
이 함수는 'address'와 같은 중첩 속성을 사용할 수 있도록 더욱 일반화할 수 있습니다.city' 또는 'style.size'를 선택합니다.폭' 등
정렬 기준 목록을 따르기만 하면 됩니다.
이 코드는 36개의 정렬 기준을 가지고 있어도 항상 판독 가능하고 이해할 수 있습니다.
여기서 Nina가 제안하는 솔루션은 확실히 매우 우아하지만, 이것은 부울 로직에서 0의 값은 false의 값에 해당하고, JavaScript에서는 boolean test가 true/false 이외의 값을 반환할 수 있다는 것을 의미하며, 이는 초보자에게는 항상 혼란스러울 수 있습니다.
또, 코드의 유지보수가 필요한 사람도 생각해 주세요.아마 당신일 것이다: 당신이 다른 사람의 코드를 며칠 동안 뒤지고 치명적인 버그가 있다고 상상해 보라. 그리고 당신은 팁으로 가득 찬 이 수천 줄들을 읽는 것에 지쳐있다.
const homes =
[ { h_id: '3', city: 'Dallas', state: 'TX', zip: '75201', price: '162500' }
, { h_id: '4', city: 'Bevery Hills', state: 'CA', zip: '90210', price: '319250' }
, { h_id: '6', city: 'Dallas', state: 'TX', zip: '75000', price: '556699' }
, { h_id: '5', city: 'New York', state: 'NY', zip: '00010', price: '962500' }
]
const fSort = (a,b) =>
{
let Dx = a.city.localeCompare(b.city) // 1st criteria
if (Dx===0) Dx = Number(b.price) - Number(a.price) // 2nd
// if (Dx===0) Dx = ... // 3rd
// if (Dx===0) Dx = ... // 4th....
return Dx
}
console.log( homes.sort(fSort))
다음은 @Snowburnt 솔루션의 일반 버전입니다.
var sortarray = [{field:'city', direction:'asc'}, {field:'price', direction:'desc'}];
array.sort(function(a,b){
for(var i=0; i<sortarray.length; i++){
retval = a[sortarray[i].field] < b[sortarray[i].field] ? -1 : a[sortarray[i].field] > b[sortarray[i].field] ? 1 : 0;
if (sortarray[i].direction == "desc") {
retval = retval * -1;
}
if (retval !== 0) {
return retval;
}
}
}
})
이것은 내가 사용하는 일종의 루틴에 근거한 것이다.이 코드를 테스트하지 않았기 때문에 오류가 있을 수 있지만 이해하실 수 있습니다.이 방법은 차이를 나타내는 첫 번째 필드를 기준으로 정렬한 후 중지하고 다음 레코드로 이동합니다.따라서 3개의 필드를 기준으로 정렬하고 비교의 첫 번째 필드가 정렬되는 두 레코드의 정렬 순서를 결정하기에 충분할 경우 해당 정렬 결과를 반환하고 다음 레코드로 이동합니다.
5000개의 레코드로 테스트를 해봤는데(실제로 좀 더 복잡한 소트 로직으로) 눈 깜짝할 사이에 완료했습니다.클라이언트에 실제로 1000개 이상의 레코드를 로드하는 경우 서버 측 정렬 및 필터링을 사용해야 합니다.
이 코드는 대소문자를 구분하지 않지만 이 사소한 수정을 독자에게 맡깁니다.
function sort(data, orderBy) {
orderBy = Array.isArray(orderBy) ? orderBy : [orderBy];
return data.sort((a, b) => {
for (let i = 0, size = orderBy.length; i < size; i++) {
const key = Object.keys(orderBy[i])[0],
o = orderBy[i][key],
valueA = a[key],
valueB = b[key];
if (!(valueA || valueB)) {
console.error("the objects from the data passed does not have the key '" + key + "' passed on sort!");
return [];
}
if (+valueA === +valueA) {
return o.toLowerCase() === 'desc' ? valueB - valueA : valueA - valueB;
} else {
if (valueA.localeCompare(valueB) > 0) {
return o.toLowerCase() === 'desc' ? -1 : 1;
} else if (valueA.localeCompare(valueB) < 0) {
return o.toLowerCase() === 'desc' ? 1 : -1;
}
}
}
});
}
사용방법:
sort(homes, [{city : 'asc'}, {price: 'desc'}])
var homes = [
{"h_id":"3",
"city":"Dallas",
"state":"TX",
"zip":"75201",
"price":"162500"},
{"h_id":"4",
"city":"Bevery Hills",
"state":"CA",
"zip":"90210",
"price":"319250"},
{"h_id":"6",
"city":"Dallas",
"state":"TX",
"zip":"75000",
"price":"556699"},
{"h_id":"5",
"city":"New York",
"state":"NY",
"zip":"00010",
"price":"962500"}
];
function sort(data, orderBy) {
orderBy = Array.isArray(orderBy) ? orderBy : [orderBy];
return data.sort((a, b) => {
for (let i = 0, size = orderBy.length; i < size; i++) {
const key = Object.keys(orderBy[i])[0],
o = orderBy[i][key],
valueA = a[key],
valueB = b[key];
if (!(valueA || valueB)) {
console.error("the objects from the data passed does not have the key '" + key + "' passed on sort!");
return [];
}
if (+valueA === +valueA) {
return o.toLowerCase() === 'desc' ? valueB - valueA : valueA - valueB;
} else {
if (valueA.localeCompare(valueB) > 0) {
return o.toLowerCase() === 'desc' ? -1 : 1;
} else if (valueA.localeCompare(valueB) < 0) {
return o.toLowerCase() === 'desc' ? 1 : -1;
}
}
}
});
}
console.log(sort(homes, [{city : 'asc'}, {price: 'desc'}]));
// custom sorting by city
const sortArray = ['Dallas', 'New York', 'Beverly Hills'];
const sortData = (sortBy) =>
data
.sort((a, b) => {
const aIndex = sortBy.indexOf(a.city);
const bIndex = sortBy.indexOf(b.city);
if (aIndex < bIndex) {
return -1;
}
if (aIndex === bIndex) {
// price descending
return b.price- a.price;
}
return 1;
});
sortData(sortArray);
몇 가지 도우미 기능을 추가하면 이러한 문제를 일반적이고 간단하게 해결할 수 있습니다. sortByKey
는 어레이 및 각 어레이 엔트리를 비교할 항목 목록을 반환하는 함수를 사용합니다.
합니다.[2] < [2, 0] < [2, 1] < [10, 0]
.
// Two helpers:
function cmp(a, b) {
if (a > b) {
return 1
} else if (a < b) {
return -1
} else {
return 0
}
}
function sortByKey(arr, key) {
arr.sort((a, b) => cmp(key(a), key(b)))
}
// A demonstration:
let arr = [{a:1, b:2}, {b:3, a:0}, {a:1, b:1}, {a:2, b:2}, {a:2, b:1}, {a:1, b:10}]
sortByKey(arr, item => [item.a, item.b])
console.log(JSON.stringify(arr))
// '[{"b":3,"a":0},{"a":1,"b":1},{"a":1,"b":10},{"a":1,"b":2},{"a":2,"b":1},{"a":2,"b":2}]'
sortByKey(arr, item => [item.b, item.a])
console.log(JSON.stringify(arr))
// '[{"a":1,"b":1},{"a":2,"b":1},{"a":1,"b":10},{"a":1,"b":2},{"a":2,"b":2},{"b":3,"a":0}]'
Python의 list.sort 함수에서 이 아이디어를 애정 어린 마음으로 훔쳤습니다.
function sortMultiFields(prop){
return function(a,b){
for(i=0;i<prop.length;i++)
{
var reg = /^\d+$/;
var x=1;
var field1=prop[i];
if(prop[i].indexOf("-")==0)
{
field1=prop[i].substr(1,prop[i].length);
x=-x;
}
if(reg.test(a[field1]))
{
a[field1]=parseFloat(a[field1]);
b[field1]=parseFloat(b[field1]);
}
if( a[field1] > b[field1])
return x;
else if(a[field1] < b[field1])
return -x;
}
}
}
사용방법(내림차순으로 정렬할 경우 -(-) sign before 필드를 입력합니다)
homes.sort(sortMultiFields(["city","-price"]));
위의 함수를 사용하여 여러 필드가 있는 임의의 json 배열을 정렬할 수 있습니다.기능 본체를 전혀 변경할 필요가 없습니다.
@chriskelly의 답변을 수정했습니다.
대부분의 답변은 값이 10,000 이하이거나 100만 이상이면 가격이 제대로 분류되지 않는다는 것을 간과하고 있습니다.JS인 Resaon은 알파벳 순으로 정렬됩니다.여기에서는 "왜 JavaScript는 5, 10, 1"을 정렬할 수 없는가"와 "정수의 배열을 올바르게 정렬하는 방법"에 대해 꽤 잘 대답했습니다.
최종적으로 소트할 필드 또는 노드가 숫자인지 어떤지를 평가해야 합니다.그런 말을 하는 게 아니라parseInt()
이 경우에는 정렬된 결과가 더 중요합니다.
var homes = [{
"h_id": "2",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"price": "62500"
}, {
"h_id": "1",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"price": "62510"
}, {
"h_id": "3",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"price": "162500"
}, {
"h_id": "4",
"city": "Bevery Hills",
"state": "CA",
"zip": "90210",
"price": "319250"
}, {
"h_id": "6",
"city": "Dallas",
"state": "TX",
"zip": "75000",
"price": "556699"
}, {
"h_id": "5",
"city": "New York",
"state": "NY",
"zip": "00010",
"price": "962500"
}];
homes.sort(fieldSorter(['price']));
// homes.sort(fieldSorter(['zip', '-state', 'price'])); // alternative
function fieldSorter(fields) {
return function(a, b) {
return fields
.map(function(o) {
var dir = 1;
if (o[0] === '-') {
dir = -1;
o = o.substring(1);
}
if (!parseInt(a[o]) && !parseInt(b[o])) {
if (a[o] > b[o]) return dir;
if (a[o] < b[o]) return -(dir);
return 0;
} else {
return dir > 0 ? a[o] - b[o] : b[o] - a[o];
}
})
.reduce(function firstNonZeroValue(p, n) {
return p ? p : n;
}, 0);
};
}
document.getElementById("output").innerHTML = '<pre>' + JSON.stringify(homes, null, '\t') + '</pre>';
<div id="output">
</div>
와, 여기 몇 가지 복잡한 해결책이 있네요.너무 복잡해서 더 간단하면서도 강력한 것을 생각해내기로 했습니다.여기 있습니다.
function sortByPriority(data, priorities) {
if (priorities.length == 0) {
return data;
}
const nextPriority = priorities[0];
const remainingPriorities = priorities.slice(1);
const matched = data.filter(item => item.hasOwnProperty(nextPriority));
const remainingData = data.filter(item => !item.hasOwnProperty(nextPriority));
return sortByPriority(matched, remainingPriorities)
.sort((a, b) => (a[nextPriority] > b[nextPriority]) ? 1 : -1)
.concat(sortByPriority(remainingData, remainingPriorities));
}
그리고 여기 사용 방법의 예가 있습니다.
const data = [
{ id: 1, mediumPriority: 'bbb', lowestPriority: 'ggg' },
{ id: 2, highestPriority: 'bbb', mediumPriority: 'ccc', lowestPriority: 'ggg' },
{ id: 3, mediumPriority: 'aaa', lowestPriority: 'ggg' },
];
const priorities = [
'highestPriority',
'mediumPriority',
'lowestPriority'
];
const sorted = sortByPriority(data, priorities);
우선 Atribute의 precedence에 따라 정렬되고 다음으로 Atribute의 값에 따라 정렬됩니다.
이게 가장 쉬운 방법일 것 같아요.
https://coderwall.com/p/ebqhca/javascript-sort-by-two-fields
매우 심플하고, 3개의 다른 키 값 쌍으로 시험해 봤는데, 효과가 좋았습니다.
다음은 간단한 예입니다. 자세한 내용은 링크를 참조하십시오.
testSort(data) {
return data.sort(
a['nameOne'] > b['nameOne'] ? 1
: b['nameOne'] > a['nameOne'] ? -1 : 0 ||
a['date'] > b['date'] ||
a['number'] - b['number']
);
}
여기 참고용으로 제 것이 있습니다. 예:
function msort(arr, ...compFns) {
let fn = compFns[0];
arr = [].concat(arr);
let arr1 = [];
while (arr.length > 0) {
let arr2 = arr.splice(0, 1);
for (let i = arr.length; i > 0;) {
if (fn(arr2[0], arr[--i]) === 0) {
arr2 = arr2.concat(arr.splice(i, 1));
}
}
arr1.push(arr2);
}
arr1.sort(function (a, b) {
return fn(a[0], b[0]);
});
compFns = compFns.slice(1);
let res = [];
arr1.map(a1 => {
if (compFns.length > 0) a1 = msort(a1, ...compFns);
a1.map(a2 => res.push(a2));
});
return res;
}
let tstArr = [{ id: 1, sex: 'o' }, { id: 2, sex: 'm' }, { id: 3, sex: 'm' }, { id: 4, sex: 'f' }, { id: 5, sex: 'm' }, { id: 6, sex: 'o' }, { id: 7, sex: 'f' }];
function tstFn1(a, b) {
if (a.sex > b.sex) return 1;
else if (a.sex < b.sex) return -1;
return 0;
}
function tstFn2(a, b) {
if (a.id > b.id) return -1;
else if (a.id < b.id) return 1;
return 0;
}
console.log(JSON.stringify(msort(tstArr, tstFn1, tstFn2)));
//output:
//[{"id":7,"sex":"f"},{"id":4,"sex":"f"},{"id":5,"sex":"m"},{"id":3,"sex":"m"},{"id":2,"sex":"m"},{"id":6,"sex":"o"},{"id":1,"sex":"o"}]
비슷한 것을 찾고 있었는데, 결국 이렇게 되었습니다.
먼저 1개 이상의 정렬 함수가 있으며 항상 0, 1 또는 -1 중 하나를 반환합니다.
const sortByTitle = (a, b): number =>
a.title === b.title ? 0 : a.title > b.title ? 1 : -1;
정렬할 서로 다른 속성에 대해 더 많은 함수를 생성할 수 있습니다.
다음으로 이러한 정렬 기능을 하나로 결합하는 기능이 있습니다.
const createSorter = (...sorters) => (a, b) =>
sorters.reduce(
(d, fn) => (d === 0 ? fn(a, b) : d),
0
);
이를 통해 위의 정렬 함수를 읽을 수 있는 방식으로 결합할 수 있습니다.
const sorter = createSorter(sortByTitle, sortByYear)
items.sort(sorter)
정렬 함수가 0을 반환하면 추가 정렬을 위해 다음 정렬 함수가 호출됩니다.
이것은 비교하기 전에 값을 포맷할 기회가 있을 때 여러 필드를 기준으로 정렬하는 재귀 알고리즘입니다.
var data = [
{
"id": 1,
"ship": null,
"product": "Orange",
"quantity": 7,
"price": 92.08,
"discount": 0
},
{
"id": 2,
"ship": "2017-06-14T23:00:00.000Z".toDate(),
"product": "Apple",
"quantity": 22,
"price": 184.16,
"discount": 0
},
...
]
var sorts = ["product", "quantity", "ship"]
// comp_val formats values and protects against comparing nulls/undefines
// type() just returns the variable constructor
// String.lower just converts the string to lowercase.
// String.toDate custom fn to convert strings to Date
function comp_val(value){
if (value==null || value==undefined) return null
var cls = type(value)
switch (cls){
case String:
return value.lower()
}
return value
}
function compare(a, b, i){
i = i || 0
var prop = sorts[i]
var va = comp_val(a[prop])
var vb = comp_val(b[prop])
// handle what to do when both or any values are null
if (va == null || vb == null) return true
if ((i < sorts.length-1) && (va == vb)) {
return compare(a, b, i+1)
}
return va > vb
}
var d = data.sort(compare);
console.log(d);
a와 b가 같을 경우 사용할 수 있을 때까지 다음 필드를 시도합니다.
lodash orderBy 함수별 lodash를 사용할 수 있습니다.
두 개의 매개 변수 배열과 방향 배열('asc', desc')이 필요합니다.
var homes = [
{"h_id":"3",
"city":"Dallas",
"state":"TX",
"zip":"75201",
"price":"162500"},
{"h_id":"4",
"city":"Bevery Hills",
"state":"CA",
"zip":"90210",
"price":"319250"},
{"h_id":"6",
"city":"Dallas",
"state":"TX",
"zip":"75000",
"price":"556699"},
{"h_id":"5",
"city":"New York",
"state":"NY",
"zip":"00010",
"price":"962500"}
];
var sorted =. data._.orderBy(data, ['city', 'price'], ['asc','desc'])
언급URL : https://stackoverflow.com/questions/6913512/how-to-sort-an-array-of-objects-by-multiple-fields
'programing' 카테고리의 다른 글
Internet Explorer에 대해 'console'이 정의되지 않은 오류입니다. (0) | 2023.01.13 |
---|---|
MySQL: int 필드의 값을 전환하는 간단한 방법 (0) | 2023.01.13 |
MySQL Workbench에서 Mariadb(Centos 7)에 액세스할 수 없음 (0) | 2023.01.13 |
명령어가 완료될 때까지 기다리지 않고 shell_exec을 사용할 수 있는 방법이 있습니까? (0) | 2023.01.13 |
PHP 정규식:끝 구분 기호 '^'을(를) 찾을 수 없습니다. (0) | 2023.01.13 |