Related
I currently have a set of strings that are both just numbers and number with + or -. Such as follows :
1 , 1+, 1-, 2, 2+, 2-, 10
Which when I sort using JavaScript's sort functions gives out:
1, 1+ , 1-, 10, 2, 2+, 2-
which is lexicographically orders but not numerically. Is there a way to sort this so the numbers come out in the correct way(the first list) ? I am using ExtJS stores so an answers as a store sorter is preferred but plain javascript is also fine. Thanks ?
Edit: This is not just sorting numbers.
You can use a custom ordering function like so:
var numbers = ['1', '1-', '1+', '2', '2+', '2-', '10'];
numbers.sort(function (a, b){
var _a = parseFloat(a), // If the values are integers only, parseInt will do too
_b = parseFloat(b);
if (_a - _b === 0) {
return (a > b) ? 1 : -1;
} else {
return _a - _b;
}
});
console.log(numbers);
The function checks whether the number values are equal, and if so, falls back to lexicographic ordering to sort the character suffixes. If there are no suffixes in equal-case, hence no matter in which order the numbers are returned. If only one of the operands has a suffix, bare number returns negative. If the number values are not equal, the function simply returns the tristate, i.e a - b, which will be evaluated to one of negative, 0, positive. Or actually it's "bistate", since we've handled 0 case already.
More generic solution
The code above is rather a special case for two different single charactered suffixes only. If suffixes are more complex, here's a more generic code to sort by number and by suffixes:
var numbers = ['1', '1-r', '1+q', '1', '2', '2+q', '2-r', '10'];
function suffixSort (suff, asc) {
asc = 2 * +(!!asc) - 1; // Convert boolean to -1 or 1
return function (a, b) {
var _a = parseFloat(a), // Extract the number value
_b = parseFloat(b),
aSI = -(a.length - _a.toString().length), // Get the index of suffix start
bSI = -(b.length - _b.toString().length);
// Equal number values, sort by suffixes
if (_a === _b) {
return (suff.indexOf(a.substr(aSI)) > suff.indexOf(b.substr(bSI))) ? 1 : -1;
}
// Inequal number values, sort by numbers
return asc * (_a - _b);
}
}
// suffixSort arguments
// suff: An array of the suffix strings to sort, ordered in the desired sorting order
// asc: true = ascending, false = descending. Optional, defaults to descending sort
numbers.sort(suffixSort(['+q', '-r'], true));
console.log(numbers);
The idea is to store the suffixes into an array, and when suffix sorting is needed, function compares the array indices of the suffixes instead of the suffixes themselves.
suffixSort lets you also to decide the sorting direction. Selected sorting direction doesn't have an effect on suffix sorting, they are always returned in the order they appear in suff array.
These values are almost integers, so comparing them according to praseInt will almost get you there. The only thing missing is a special treatment for values that have the same integer part where x- should come first, then x and finally x+:
function specialChar(s) {
c = s.substr(-1);
if (c == '+') {
return 1;
}
if (c == '-') {
return -1;
}
return 0;
}
function numCompare(a, b) {
aNum = parseInt(a);
bNum = parseInt(b);
cmp = aNum - bNum;
if (cmp != 0) {
return cmp;
}
// Integer parts are equal - compare the special char at the end
return specialChar(a) - specialChar(b);
}
arr = ['1' , '1+', '1-', '2', '2+', '2-', '10'];
arr.sort(numCompare);
var result=[];
result=array.map(function(n){
if(typeof n==='number') return n;
if(n[n.length-1]=='+'){
return parseInt(n.substring(0,n.length-1))
}
else if(n[n.length-1]=='-'){
return 0-parseInt(n.substring(0,n.length-1))
}
});
result.sort(function(a,b){return a-b})
You could use Array#sort and split the elements in numbers and the rest, then return the difference or the difference of the order.
var array = ['10', '2', '2+', '2-', '1', '1+', '1-'];
array.sort(function (a, b) {
var r = /\d+|\D+/g,
aa = a.match(r),
bb = b.match(r),
order = { '+': 1, '-': 2 };
return aa[0] - bb[0] || (order[aa[1]] || 0) - (order[bb[1]] || 0);
});
console.log(array);
If there are only three possible states of a number, and the states have the order number, number+, number the states can be recreated by creating an array representation of the numbers, removing the unique numbers from array, from minimum to maximum, concatenating empty string or arithmetic operator in required order to the number, then pushing the value to an array, where .toString() can be used to view the comma separated string representation of the sorted values within the array
var str = `314+, 1-, 7+, 1, 1-, 271-, 10-
, 10+, 271, 271+, 314-, 314
, 10, 2-, 2, 2+, 7-, 7`;
for (var [nums, order, res, num] = [str.match(/\d+/g), ["", "+", "-"], [], null]
; nums.length
; num = Math.min.apply(Math, nums)
, res = [...res, ...order.map(op => num + op)]
, nums = nums.filter(n => n != num)
);
console.log(res.toString() + "\n", res);
Assuming that you just want to throw away the symbols, then you could use parseInt and Array#sort to get order numerically.
var data = ['1' , '1+', '1-', '2', '2+', '2-', '10'];
var sortedData = data.sort(function(a,b){return parseInt(a)-parseInt(b);});
I have an array of strings like below.
ABC
QRS
DEF
HIJ
TUV
KLM
NOP
I need to sort this array in javascript in alphabetical order, except for few values which is already known. ie I need DEF and NOP comes in the first 2 positions and sort rest of the array alphabetically in ascending order. Here is what I've written to sort the entire array in alphabetical order, now I need the 2 values in the first 2 positions.
array.sort(function(a,b){return ((a < b) ? -1 : (a > b) ? 1 : 0)});
Expected result.
DEF
NOP
ABC
HIJ
KLM
QRS
TUV
The contents of the array is dynamic, so if the array has DEF or NOP, then those should be on top, if else, it should be sorted alphabetically. Whats the best way to approach this?
I think the most straightforward way would be to remove the known elements separately instead of trying to incorporate them into the sort. That way, you can also just sort without a comparison function.
function sortWithKnownPrefix(prefix, arr) {
// Get the non-prefix elements
var rest = arr.filter(function (item) {
return prefix.indexOf(item) === -1;
});
// Concatenate the prefix and the sorted non-prefix elements
return prefix.concat(rest.sort());
}
sortWithKnownPrefix(
["DEF", "NOP"],
["ABC", "QRS", "DEF", "HIJ", "TUV", "KLM", "NOP"]
)
// ["DEF", "NOP", "ABC", "HIJ", "KLM", "QRS", "TUV"]
If you want to allow for, count and hoist multiple instances of those strings: http://jsfiddle.net/4hgnjqas/1/
The following will count all known instances of the strings you want to hoist to the front, remove them from the existing array, sort it, then add the same number of "DEF"s and "NOP"s to the array.
var hoist = {"NOP":0, "DEF":0}
for(var p in hoist)
while(array.indexOf(p)>-1){
array.splice(array.indexOf(p),1);
hoist[p]++;
}
arr.sort();
for(var p in hoist){
for(var i=0;i<hoist[p];i++)
array.unshift(p);
}
console.log(array);
This will reposition 'DEF' and 'NOP' after sorting the rest of the items:
function customSort(array) {
array = array.sort(function(a,b){
return (a < b) ? -1 : (a > b) ? 1 : 0;
});
var NOPIndex = array.indexOf('NOP');
if (NOPIndex > -1)
array.unshift(array.splice(NOPIndex, 1)[0]);
var DEFIndex = array.indexOf('DEF');
if (DEFIndex > -1)
array.unshift(array.splice(DEFIndex, 1)[0]);
return array;
}
this could be the simpler one
var arr = ['ABC','QRS','DEF','HIJ','TUV','KLM','NOP']
var exclude = ['DEF', 'NOP'];
arr.sort(function(a,b) {
if(exclude.indexOf(a) > -1 && exclude.indexOf(b) > -1)
return ((a < b) ? -1 : (a > b) ? 1 : 0);
if(exclude.indexOf(b) > -1)
return 1;
return ((a < b) ? -1 : (a > b) ? 1 : 0)
});
alert(JSON.stringify(arr)) //result
JS Fiddle *UPDATED
I have an array that contains numbers and strings:
disorderedArray = ["74783 Banana", "38903 Orange", "94859 Apple"];
I needed to put them in ascending order by number. I found an example that worked well for descending Sort Alphanumeric String Descending
However, I can't seem to change the return line to make the array ascending. I tried to put arr.reverse(); after the return line, but that seemed kind of hackish and it didn't work anyways. I also changed the > and < symbols in the return line, but I started to get crazy results.
function sort() {
var arr=disorderedArray;
arr.sort(function(a,b){
a=a.split(" ");
b=b.split(" ");
var an=parseInt(a[0],10);
var bn=parseInt(b[0],10);
return an<bn?1:(an>bn?-1:(a[1]<b[1]?-1:(a[1]>b[1]?1:0)));
arr.reverse();
});
console.log(arr);
}
The callback function returns positive or negative values depending on the comparison of the values. Just change the sign of the -1 and 1 values that are returned:
return an<bn?-1:(an>bn?1:(a[1]<b[1]?1:(a[1]>b[1]?-1:0)));
Here is a more readable way to write the same:
return (
an < bn ? -1 :
an > bn ? 1 :
a[1] < b[1] ? 1 :
a[1] > b[1] ? -1 :
0
);
Note that the original code sorts the numeric part descending and the rest of the string ascending, so this does the opposite. The value from the first two comparisons determine how the numeric part is sorted, and the last two determine how the rest of the strings are sorted, so you can adjust them to get the desired combination.
sort's function is just to sort. You need to call reverse on the sorted array.
function sort() {
var arr = disorderedArray;
arr.sort(function(a, b) {
a = a.split(" ");
b = b.split(" ");
var an = parseInt(a[0], 10);
var bn = parseInt(b[0], 10);
return an < bn ? 1 : (an > bn ? -1 : (a[1] < b[1] ? -1 : (a[1] > b[1] ? 1 : 0)));
});
console.log(arr.reverse());
}
I have an array of objects, in which each contains several numerical values. Here is the layout of the object:
{
v: [],
cm: 0
}
This is the definition of the sort function:
registry.sort(function(a,b) {return (a.cm > b.cm) ? 1 : ((b.cm > a.cm) ? -1 : 0);} );
When I run the sort function, it does nothing whatsoever, just leaves the array the way it was. I am new to js, but as far as I can tell, everything should be working. Does anyone know what is wrong?
Thanks
EDIT: here is the smallest example I can extract
var registry = [];
registry.sort(function(a,b) {return (a.cm > b.cm) ? 1 : ((b.cm > a.cm) ? -1 : 0);} );
var draw = function() {
//various other function calls that add values to the registry array
registry[0] = {v:[0,0,0], cm:3}; //just to have something to use in the variable
registry[1] = {v:[0,0,0], cm:2};
debug(registry[0], registry[1]);
registry.sort();
debug(registry[0], registry[1]);
}
The sort() is not a configuration. When you call it, it does the sort. You can do this:
var comparator = function(a,b) {return (a.cm > b.cm) ? 1 : ((b.cm > a.cm) ? -1 : 0);}
for (var i = 0; i < regPos; i++) {
debug(registry[i], "eah");
}
registry.sort(comparator);
registry.reverse();
for (var i = 0; i < regPos; i++) {
debug(registry[i]);
}
Or simply
var comparator = function(a,b) {return a.cm - b.cm;}
The code below will sort the array "points" which has a similar structure... not sure if this is an exact match for your data though.
var points = [{v:[1,2,3],cm:40},{v:[2], cm:100},{v:[], cm:1},{cm:5,v:[]},{v:[3],cm:25},{cm:10}];
points.sort(function(a,b) {return (a.cm - b.cm);});
for (x in points) {
console.log(points[x]);
}
Are you sure the "cm" field is defined for every record in your array?
I have the following array in JavaScript:
myArray = ["lu9","lu10","lu11","ma9","ma10","ma11","mi9","mi10","mi11"];
Then, I need to display the values (for example in an alert) but must be arranged as follows:
"lu9,ma9,mi9,lu10,ma10,mi10,lu11,ma11,mi11"
How I can do this?
Each item in your list has two parts: The leading mishmash of characters (mi, ma, lu) and a numerical suffix. To properly sort, we have to take both into account.
array.sort(function sorter (a, b) {
var re = /^(\D+)(\d+)$/,
left = re.exec(a),
right = re.exec(b);
if (left[1] === right[1]) {
return Number(left[2]) - Number(right[2]);
}
return left[1] < right[1] ? -1 : 1;
});
Let's say a = lu9 and b = lu10:
1. left = ['lu9', 'lu', '9']
2. right = ['lu10', 'lu', '10']
3. left[1] === right[1]
1. Number(left[2]) = 9
2. Number(right[2]) = 10
3. return 9 - 10 (negative number, a before b)
Now if our input is a = lu9 and b = mi4:
1. left = ['lu9', 'lu', '9']
2. right = ['mi4', 'mi', '4']
3. left[1] !== right[1]
1. left[1] < right[1] = true
2. return -1
var myArray = ["lu9", "lu10", "lu11", "ma9", "ma10", "ma11", "mi9", "mi10", "mi11"];
function myResult(myArray) {
myArray = myArray.slice().sort(function (a, b) {
var reg = /\d+/ //A regex to extract the numerical part
var num = 2 * (+a.match(reg) - +b.match(reg)) //Put a weight of 2 on the numerical value
var str = a > b ? 1 : a < b ? -1 : 0 //The strings value with a single weight
return num + str //add them and we have a positive or negative value with a correct weight on the numerical part
})
return "" + myArray
}
console.log (myResult(myArray)) //"lu9,ma9,mi9,lu10,ma10,mi10,lu11,ma11,mi11"
Heres a Fiddle
var myArray = ["lu9","lu10","lu11","ma9","ma10","ma11","mi9","mi10","mi11"];
var derp = function(a, b) {
a = a.replace(/[^0-9]+/g, '', a);
b = b.replace(/[^0-9]+/g, '', b);
return a < b;
}
myArray.sort(derp);
We need to sort first by number and then by letters.
no need for regex here.
We will use padding:
so ma11 will be 0011ma
and mi11 will be 0011mi
and ma11 will be 0011ma
(and mi9 will be 0009mi , the padding helps 11 to be bigger then 2 as string)
so sorting it now - will yield the right result.
var a = ["ma9", "ma10", "ma11", "mi9", "mi10", "mi11", "lu9", "lu10", "lu11"]
a.sort(function (a, b)
{
return calc(a) > calc(b);
});
function calc(x)
{
return ("0000" + x.slice(2)).slice(-4) + x.slice(0,2);
}
result :
["ma9", "ma10", "ma11", "mi9", "mi10", "mi11", "lu9", "lu10", "lu11"]