밑줄 : 여러 속성에 따른 sortBy ()
여러 속성을 기반으로 객체로 배열을 정렬하려고합니다. 즉, 첫 번째 특성이 두 개체간에 동일하면 두 번째 특성을 사용하여 두 개체를 비교해야합니다. 예를 들어 다음 배열을 고려하십시오.
var patients = [
[{name: 'John', roomNumber: 1, bedNumber: 1}],
[{name: 'Lisa', roomNumber: 1, bedNumber: 2}],
[{name: 'Chris', roomNumber: 2, bedNumber: 1}],
[{name: 'Omar', roomNumber: 3, bedNumber: 1}]
];
roomNumber
속성 별로 정렬 하면 다음 코드를 사용합니다.
var sortedArray = _.sortBy(patients, function(patient) {
return patient[0].roomNumber;
});
이것은 잘 작동하지만 'John'과 'Lisa'가 올바르게 정렬되도록 어떻게 진행합니까?
sortBy
그것은 안정적인 정렬 알고리즘이므로 두 번째 속성으로 먼저 정렬 한 다음 다음과 같이 첫 번째 속성으로 다시 정렬해야합니다.
var sortedArray = _(patients).chain().sortBy(function(patient) {
return patient[0].name;
}).sortBy(function(patient) {
return patient[0].roomNumber;
}).value();
두 번째 sortBy
는 John과 Lisa가 같은 방 번호를 가지고 있음을 발견하면 찾은 순서대로 유지합니다. 첫 번째 sortBy
는 "Lisa, John"으로 설정됩니다.
다음과 같은 경우에 때때로 사용하는 해킹 트릭이 있습니다. 결과를 정렬 할 수있는 방식으로 속성을 결합하십시오.
var sortedArray = _.sortBy(patients, function(patient) {
return [patient[0].roomNumber, patient[0].name].join("_");
});
그러나 내가 말했듯이, 그것은 꽤 해키입니다. 이를 올바르게 수행하려면 실제로 핵심 JavaScript sort
메소드를 사용 하고 싶을 것입니다 .
patients.sort(function(x, y) {
var roomX = x[0].roomNumber;
var roomY = y[0].roomNumber;
if (roomX !== roomY) {
return compare(roomX, roomY);
}
return compare(x[0].name, y[0].name);
});
// General comparison function for convenience
function compare(x, y) {
if (x === y) {
return 0;
}
return x > y ? 1 : -1;
}
물론, 이 자리에 배열을 정렬합니다. 정렬 된 사본을 원한다면 (예 _.sortBy
를 들어) 배열을 먼저 복제하십시오.
function sortOutOfPlace(sequence, sorter) {
var copy = _.clone(sequence);
copy.sort(sorter);
return copy;
}
권태 중, 난 그냥뿐만 아니라 이것에 대한 (키의 임의의 수를 기준으로 정렬) 일반적인 솔루션을 썼다 보고 있습니다 .
I know I'm late to the party, but I wanted to add this for those in need of a clean-er and quick-er solution that those already suggested. You can chain sortBy calls in order of least important property to most important property. In the code below I create a new array of patients sorted by Name within RoomNumber from the original array called patients.
var sortedPatients = _.chain(patients)
.sortBy('Name')
.sortBy('RoomNumber')
.value();
btw your initializer for patients is a bit weird, isn't it? why don't you initialize this variable as this -as a true array of objects-you can do it using _.flatten() and not as an array of arrays of single object, maybe it's typo issue):
var patients = [
{name: 'Omar', roomNumber: 3, bedNumber: 1},
{name: 'John', roomNumber: 1, bedNumber: 1},
{name: 'Chris', roomNumber: 2, bedNumber: 1},
{name: 'Lisa', roomNumber: 1, bedNumber: 2},
{name: 'Kiko', roomNumber: 1, bedNumber: 2}
];
I sorted the list differently and add Kiko into Lisa's bed; just for fun and see what changes would be done...
var sorted = _(patients).sortBy(
function(patient){
return [patient.roomNumber, patient.bedNumber, patient.name];
});
inspect sorted and you'll see this
[
{bedNumber: 1, name: "John", roomNumber: 1},
{bedNumber: 2, name: "Kiko", roomNumber: 1},
{bedNumber: 2, name: "Lisa", roomNumber: 1},
{bedNumber: 1, name: "Chris", roomNumber: 2},
{bedNumber: 1, name: "Omar", roomNumber: 3}
]
so my answer is : use an array in your callback function this is quite similar to Dan Tao's answer, I just forget the join (maybe because I removed the array of arrays of unique item :))
Using your data structure, then it would be :
var sorted = _(patients).chain()
.flatten()
.sortBy( function(patient){
return [patient.roomNumber,
patient.bedNumber,
patient.name];
})
.value();
and a testload would be interesting...
None of these answers are ideal as a general purpose method for using multiple fields in a sort. All of the approaches above are inefficient as they either require sorting the array multiple times (which, on a large enough list could slow things down a lot) or they generate huge amounts of garbage objects that the VM will need to cleanup (and ultimately slowing the program down).
Here's a solution that is fast, efficient, easily allows reverse sorting, and can be used with underscore
or lodash
, or directly with Array.sort
The most important part is the compositeComparator
method, which takes an array of comparator functions and returns a new composite comparator function.
/**
* Chains a comparator function to another comparator
* and returns the result of the first comparator, unless
* the first comparator returns 0, in which case the
* result of the second comparator is used.
*/
function makeChainedComparator(first, next) {
return function(a, b) {
var result = first(a, b);
if (result !== 0) return result;
return next(a, b);
}
}
/**
* Given an array of comparators, returns a new comparator with
* descending priority such that
* the next comparator will only be used if the precending on returned
* 0 (ie, found the two objects to be equal)
*
* Allows multiple sorts to be used simply. For example,
* sort by column a, then sort by column b, then sort by column c
*/
function compositeComparator(comparators) {
return comparators.reduceRight(function(memo, comparator) {
return makeChainedComparator(comparator, memo);
});
}
You'll also need a comparator function for comparing the fields you wish to sort on. The naturalSort
function will create a comparator given a particular field. Writing a comparator for reverse sorting is trivial too.
function naturalSort(field) {
return function(a, b) {
var c1 = a[field];
var c2 = b[field];
if (c1 > c2) return 1;
if (c1 < c2) return -1;
return 0;
}
}
(All the code so far is reusable and could be kept in utility module, for example)
Next, you need to create the composite comparator. For our example, it would look like this:
var cmp = compositeComparator([naturalSort('roomNumber'), naturalSort('name')]);
This will sort by room number, followed by name. Adding additional sort criteria is trivial and does not affect the performance of the sort.
var patients = [
{name: 'John', roomNumber: 3, bedNumber: 1},
{name: 'Omar', roomNumber: 2, bedNumber: 1},
{name: 'Lisa', roomNumber: 2, bedNumber: 2},
{name: 'Chris', roomNumber: 1, bedNumber: 1},
];
// Sort using the composite
patients.sort(cmp);
console.log(patients);
Returns the following
[ { name: 'Chris', roomNumber: 1, bedNumber: 1 },
{ name: 'Lisa', roomNumber: 2, bedNumber: 2 },
{ name: 'Omar', roomNumber: 2, bedNumber: 1 },
{ name: 'John', roomNumber: 3, bedNumber: 1 } ]
The reason I prefer this method is that it allows fast sorting on an arbitrary number of fields, does not generate a lot of garbage or perform string concatenation inside the sort and can easily be used so that some columns are reverse sorted while order columns use natural sort.
Simple Example from http://janetriley.net/2014/12/sort-on-multiple-keys-with-underscores-sortby.html (courtesy of @MikeDevenney)
Code
var FullySortedArray = _.sortBy(( _.sortBy(array, 'second')), 'first');
With Your Data
var FullySortedArray = _.sortBy(( _.sortBy(patients, 'roomNumber')), 'name');
Perhaps underscore.js or just Javascript engines are different now than when these answers were written, but I was able to solve this by just returning an array of the sort keys.
var input = [];
for (var i = 0; i < 20; ++i) {
input.push({
a: Math.round(100 * Math.random()),
b: Math.round(3 * Math.random())
})
}
var output = _.sortBy(input, function(o) {
return [o.b, o.a];
});
// output is now sorted by b ascending, a ascending
In action, please see this fiddle: https://jsfiddle.net/mikeular/xenu3u91/
Just return an array of properties you want to sort with:
ES6 Syntax
var sortedArray = _.sortBy(patients, patient => [patient[0].name, patient[1].roomNumber])
ES5 Syntax
var sortedArray = _.sortBy(patients, function(patient) {
return [patient[0].name, patient[1].roomNumber]
})
This does not have any side effects of converting a number to a string.
You could concatenate the properties you want to sort by in the iterator:
return [patient[0].roomNumber,patient[0].name].join('|');
or something equivalent.
NOTE: Since you are converting the numeric attribute roomNumber to a string, you would have to do something if you had room numbers > 10. Otherwise 11 will come before 2. You can pad with leading zeroes to solve the problem, i.e. 01 instead of 1.
I think you'd better use _.orderBy
instead of sortBy
:
_.orderBy(patients, ['name', 'roomNumber'], ['asc', 'desc'])
If you happen to be using Angular, you can use its number filter in the html file rather than adding any JS or CSS handlers. For example:
No fractions: <span>{{val | number:0}}</span><br>
In that example, if val = 1234567, it will be displayed as
No fractions: 1,234,567
Example and further guidance at: https://docs.angularjs.org/api/ng/filter/number
참고URL : https://stackoverflow.com/questions/16426774/underscore-sortby-based-on-multiple-attributes
'programing tip' 카테고리의 다른 글
특정 열을 선택하는 Spring JPA (0) | 2020.07.26 |
---|---|
rc.exe로 인해 Visual Studio를 빌드 할 수 없습니다 (0) | 2020.07.26 |
모든 플랫폼에서 이온 모드에서만 앱을 세로 모드로 제한하는 방법은 무엇입니까? (0) | 2020.07.26 |
PHP7 : ext-dom 문제 설치 (0) | 2020.07.26 |
안드로이드 디렉토리에 파일을 나열하는 방법? (0) | 2020.07.26 |