How the JavaScript object array is sorted by the specified attribute and sort direction

  • 2021-06-28 11:04:02
  • OfStack

Introduction

In data-centric information systems, it is common to present data in tabular form.Sorting data is an essential function.Sorting can be divided into sorting by a single field and sorting by multiple fields in different sorting directions.Single-field sorting has a large limitation and can not meet the user's needs for changing the focus of data, while multi-field sorting can better make up for this defect.

Multi-field sorting, which can be implemented in a large way, can be divided into back-end implementation and front-end implementation.

Backend Sorting

Sorting on the back end can be done at the database level or at the application level.

Multi-field sorting at the database level is very simple, using SQL's sorting directive "Order By" - Order By field1 asc, field2 desc, field3 asc --....
The application level refers to the Web application layer (the C/S architecture is not discussed here), such as PHP, Java Web, ASP.NET, etc.Application-level implementations use PHP, Java,.NET (C#/VB) back-end service languages to sort data.Take ASP.NET C # for example, because LINQ in C # has built-in operations on collection types and supports multi-attribute sorting, it is easy to do this with LINQ - from f in foos orderby f.Name descending, f.Num ascending select (you can see that LINQ's sorting syntax is almost the same as SQL's).If other languages do not have similar built-in support, they are implemented by sorting algorithms, which are generic and independent of the programming language.

Front Sort

In JavaScript, the array has a sort method, sort, which is convenient to use when the array is a simple array (array elements are simple types -- strings, numbers, and Booleans).However, when an array element is of a non-simple type, such as Object for name/value pairs, and you want to sort in different order directions by a few specified attributes, a simple call to the "sort" method will not do this.

Fortunately, the "sort" method reserves an interface for customizing sorting to achieve the desired sorting.

See how the array's "sort" method works.

sort Function Prototype


//  Sort the elements of an array in place and return the array. 
//  Default by string Unicode Code Location ( code point ) Sort. 
Array.prototype.sort([compareFunction]:number); // number : -1 | 0 | 1 . 
//  Typical comparison function (ascending sort). 
function compareFunction(item1, item2) {
if(item1 > item2) {
return 1; //  If it is a descending sort, return -1 . 
}else if(item1 === item2) {
return 0;
}else {
return -1; //  If it is a descending sort, return 1 . 
}
}

Description: If compareFunction is not specified, then the elements are converted to characters of a string and sorted in the Unicode locus order.For example, "Cherry" would be placed before "banana".When sorting numbers, 9 occurs before 80 because they are first converted to strings and "80" is ahead of "9".

&8226;If compareFunction (a, b) is less than 0, then a will be ranked before b;

&8226;If compareFunction (a, b) equals 0, a and b

The relative position does not change.Note: The ECMAScript standard does not guarantee this behavior, nor does it apply to all browsers (e.g. Mozilla in 2003)
Version before year;

&8226;If compareFunction (a, b) is greater than 0, ES106 EN will be ranked before a.

&8226;compareFunction (a, b) must always return the same comparison results for the same input, otherwise the ordered results will be indeterminate.

Note: The sorting result obtained by the above rules is ascending. If you want to get the descending result, you can return the result less than 0 when the comparison result is greater than 0 and the result greater than 0 when the comparison result is less than 0.

To achieve multi-attribute sorting, the key lies in the implementation of the comparison function.Based on the above rules, to achieve multi-attribute sorting in different directions, the size relationship of two comparison items is still returned.

How do we determine the size relationship of many attribute objects?This can be done in two steps.

Step 1 records the results of the comparison of the two sorting items according to each sorting attribute and direction.


var propOrders = { "prop1":"asc", "prop2":"desc", "prop3":"asc"};
function cmp(item1, item2, propOrders) {
var cps = []; //  Used to record the comparison results for each sorting attribute. -1 | 0 | 1  . 
var isAsc = true; //  Sort direction.  
for(var p in propOrders) {
isAsc = propOrders[p] === "asc";
if(item1[p] > item2[p]) {
cps.push(isAsc ? 1 : -1);
break; //  You can jump out of the loop because you already know it here  item1  "Greater than"  item2  Yes. 
} else if(item1[p] === item2[p]) {
cps.push(0);
} else {
cps.push(isAsc ? -1 : 1);
break; //  You can jump out of the loop. item1  "Less than"  item2 . 
} 
} 
/*
.
.
.
*/
}

In the second step, the final size relationship of the two comparison items is synthesized according to the comparison results of the sorting attributes.


/* 
.
.
. 
*/
for(var j = 0; j < cps.length; j++) {
if(cps[j] === 1 || cps[j] === -1) {
return cps[j];
}
}
return 0;

With these ideas, it is easy to implement the whole comparison function. Here is the complete JavaScript code for the comparison function:

Comparison function


function SortByProps(item1, item2) {
"use strict";
var props = [];
for (var _i = 2; _i < arguments.length; _i++) {
props[_i - 2] = arguments[_i];
}
var cps = []; //  Stores sort attribute comparison results. 
//  If no sort attribute is specified, the order is ascending by the full attribute.  
var asc = true;
if (props.length < 1) {
for (var p in item1) {
if (item1[p] > item2[p]) {
cps.push(1);
break; //  Jump out of the loop when larger than. 
} else if (item1[p] === item2[p]) {
cps.push(0);
} else {
cps.push(-1);
break; //  Jump out of the loop when it is less than. 
}
}
} else {
for (var i = 0; i < props.length; i++) {
var prop = props[i];
for (var o in prop) {
asc = prop[o] === "asc";
if (item1[o] > item2[o]) {
cps.push(asc ? 1 : -1);
break; //  Jump out of the loop when larger than. 
} else if (item1[o] === item2[o]) {
cps.push(0);
} else {
cps.push(asc ? -1 : 1);
break; //  Jump out of the loop when it is less than. 
}
}
}
} 
for (var j = 0; j < cps.length; j++) {
if (cps[j] === 1 || cps[j] === -1) {
return cps[j];
}
}
return 0; 
}

test case


// ------------- test case ------------------------------
var items = [ { name: 'Edward', value: 21 },
{ name: 'Sharpe', value: 37 },
{ name: 'And', value: 45 },
{ name: 'Edward', value: -12 },
{ name: 'Magnetic', value: 21 },
{ name: 'Zeros', value: 37 }
];
function test(propOrders) {
items.sort(function (a, b) {
return SortByProps(a, b, propOrders);
});
console.log(items);
}
function testAsc() {
test({ "name": "asc", "value": "asc" });
}
function testDesc() {
test({ "name": "desc", "value": "desc" });
}
function testAscDesc() {
test({ "name": "asc", "value": "desc" });
}
function testDescAsc() {
test({ "name": "desc", "value": "asc" });
} 
TypeScript Code 
/**
**  Sort direction. 
*/
type Direct = "asc" | "desc";
/**
**  Sort properties. 
** 
** @interface IPropertyOrder
*/
interface IPropertyOrder { 
[name: string] : Direct;
}
/**
**  Simple name / Value object. 
** 
** @interface ISimpleObject
*/
interface ISimpleObject {
[name: string] : string | number | boolean;
}
/**
**  For simple names / Value objects are sorted by the specified attribute and sorting direction (according to the sorting attribute and direction, 
**  Compare the two items in turn and return the value representing the sorting position. 
** 
** @template T  Simple name / Value object. 
** @param {T} item1  Sort comparison items 1 . 
** @param {T} item2  Sort comparison items 2 . 
** @param {...IPropertyOrder[]} props  Sort properties. 
** @returns  If Item 1 Greater than item 2 Return 1 If 1 Equal Item 2 Return 0 Otherwise return -1 . 
*/
function SortByProps<T extends ISimpleObject>
(item1: T, item2: T, ...props: IPropertyOrder[]) {
"use strict";
var cps: Array<number> = []; //  Stores sort attribute comparison results. 
//  If no sort attribute is specified, the order is ascending by the full attribute.  
var asc = true;
if (props.length < 1) {
for (var p in item1) {
if (item1[p] > item2[p]) {
cps.push(1);
break; //  Jump out of the loop when larger than. 
} else if (item1[p] === item2[p]) {
cps.push(0);
} else {
cps.push(-1);
break; //  Jump out of the loop when it is less than. 
}
}
} else { //  Sort by specified attribute and direction of rise and fall. 
for (var i = 0; i < props.length; i++) {
var prop = props[i];
for (var o in prop) {
asc = prop[o] === "asc";
if (item1[o] > item2[o]) {
cps.push(asc ? 1 : -1);
break; //  Jump out of the loop when larger than. 
} else if (item1[o] === item2[o]) {
cps.push(0);
} else {
cps.push(asc ? -1 : 1);
break; //  Jump out of the loop when it is less than. 
}
}
}
}
for (var j = 0; j < cps.length; j++) {
if (cps[j] === 1 || cps[j] === -1) {
return cps[j];
}
}
return 0; 
}

Use scenarios and limitations

Using JavaScript on the front end for multi-attribute sorting reduces server-side requests and server-side computational pressure, but it is also only applicable when only local data needs to be sorted.If multiple attribute sorting is required for the entire dataset, it is ultimately done at the server-side database level.


Related articles: