Sort array of objects following multiple rules in javascript - javascript

So I have an array of objects, and I would like to sort them following these two rules (in order of priority):
The numbers must be in numerical order
The times must be in chronological order
So, I want the objects not only to be sorted by numbers, but to be also be sorted by time.
For example, this would be ok.
005: 2am
005: 3am
005: 4am
006: 2am
006: 3am
This is the structure of the part of the objects that interests us:
var notSortedData = {
number: number, // it's a string
scheduled_date: scheduled_date, // the format is "YYYY-MM-DD HH:MM:SS"
}
sortedTrains.push(notSortedData);
So, notSortedData is pushed in sortedTrains via a for loop. Then I do this, but it is not enough (as it doesn't respect my second condition):
// sorts all the numbers numerically
sortedTrains.sort(function(a, b) {
return parseInt(a.number) - parseInt(b.number);
});
What do I need to do to make sure that my second condition is also respected? Thanks!

You can try this:
sortedTrains.sort(function(a, b) {
// We parse the numbers
var num1 = parseInt(a.number), num2 = parseInt(b.number);
if (num1 != num2) return num1 - num2; // Return the difference IF they are not equal
var date1 = new Date(a.scheduled_date), date2 = new Date(b.scheduled_date);
// We only get here if the numbers are equal
return date1 - date2;
});

I would suggest to utilize a library such as underscore or lo-Dash, because later you might add more sort conditions or change them and using the library will greatly reduce the noisiness of your code and save you development effort. Assuming lo-Dash is used, the code will be as elegant as follows:
var sortedTrains = [
{ 'number': 5, 'scheduled_date': '2014-10-12 00:00:00'},
{ 'number': 5, 'scheduled_date': '2014-10-12 01:00:00' },
{ 'number': 5, 'scheduled_date': '2014-10-12 02:00:00'},
{ 'number': 6, 'scheduled_date': '2014-10-12 03:00:00' }
];
var result = _.sortBy(sortedTrains, ['number', 'scheduled_date']);

Related

Sorting object by numbers [duplicate]

This question already has answers here:
How to sort an array of objects with with values that contain numbers and string [closed]
(2 answers)
Closed 15 days ago.
I'm trying to sort my array one of two ways, first by overhead Ascending and then by amount Ascending.
The following code works fine for the overhead which is text but replacing a.overhead with a.amount doesn't work correctly. It displays a 1,11,15,2,22,24,3,33,35 etc where as it should be 1,2,3,11,15,22,24 etc.
sortedArray = [...amendedArray].sort((a,b)=>(a.Overhead.toLowerCase() > b.Overhead.toLowerCase()) ? 1 : ((b.Overhead.toLowerCase() > a.Overhead.toLowerCase()) ? -1 : 0));
I believe I need to use function for numbers but I got help with the above code and I can't seem to convert it to make it work in my scenario.
sortedArray = [...amendedArray].sort(function(a, b){ return a.Amount - b.Amount });
You can use a collator that can handle comparison of both strings and numbers
const collator = new Intl.Collator("en", {
sensitivity: 'base', // ignore case and accents
numeric: true // treat numeric strings as numbers, "1" < "2" < "10"
});
sortedArray = [...amendedArray].sort((a, b) => {
return collator.compare(a.Overhead, b.Overhead) || collator.compare(a.Amount, b.Amount)
})
You can read more about the Intl.Collator and the options

Group nearby dates together

I have a list of variable dates that I would like to group together, preferably in javascript.
ie.
2014-08-12
2014-08-10
2014-07-28
2014-07-27
2014-01-27
2013-04-27
2003-02-12
This list of days can be completely dynamic, but here is an example resultset.
Can anyone think of an elegant way to group dates that are considered to be 'near' each other together, which in this case would be:
2014-08-12
2014-08-10
2014-07-28
2014-07-27
2014-01-27
2013-04-27
2003-02-12
A way to do this is:
Convert all date strings to numbers using .getTime()
Sort
Group
Convert back to date string
An example of this:
var dates = ['2014-08-12', '2014-08-10', '2014-07-28', '2014-07-27', '2014-01-27', '2013-04-27', '2003-02-12'],
groups = [],
last = 0
dates.map(function (each) {
return new Date(each).getTime() // 1.
}).sort(function (a, b) {
return b-a // 2.
}).forEach(function (each) {
if (Math.abs(each-last) > 1e10) { // 1e10 ms = 116 days
groups.push([]) // 3.
}
groups[groups.length-1].push(each)
last = each
})
groups.map(function (dates) {
return dates.map(function (each) {
return new Date(each).toISOString().substr(0, 10) // 4.
}).join('\n')
}).join('\n\n')
You can adjust the 1e10 value to whatever nearby dates means to your application

Find minimum and maximum dates

There is an array:
var a = new Array();
It contains date entries like this: '2012-09-12 09:20', etc
I need to find minimum and maximum dates using javascript. This code does not work with time values.
var minT = Math.min.apply(Math, a);
var maxT = Math.max.apply(Math, a);
How can I solve this problem in javascript? It seems to be quite complex as I'm not very experienced in this language.
If your array contains Date objects, then this should work. If it just contains strings like '2012-09-12 09:20', then you can sort them, and get the 1st and last elements.
a.sort(function(a, b){
return Date.parse(a) - Date.parse(b);
});
var maxT = a[a.length-1];
var minT = a[0];
Math.min/max only compares numbers, not strings. Don't use them to represent the dates, but use Date objects - they will be compared by their internal timestamp number. Still, the max/min will return that internal number, so you would need to convert it back to a Date (see Min/Max of dates in an array?):
However, if you want to use the strings or can't use the recreated Date, you will need to run manually through the array - either with a for-loop, or the ES5.1-only iterator method .reduce():
var min = datestrings.reduce(function(min, cur) {
return cur < min ? cur : min;
});
// is equivalent to
var min = datestrings[0];
for (var i=1; i<datestrings.length; i++)
if (datestrings[i] < min)
min = datestrings[i];
If your code does not need to be efficient, you also just can sort the array and get the first and last values. The default alphanumeric sorting will do it for your date format, so this is really simple:
datestrings.sort();
var min = datestrings[0],
max = datestrings[datestrings.lengh-1];
Try this:
var maxDate=new Date(Math.max.apply(null,dates));
var minDate=new Date(Math.min.apply(null,dates));
I found it on an earlier question
This should do it:
var maxT=new Date(Math.max.apply(null,a));
var minT=new Date(Math.min.apply(null,a));
If you must work with strings you could define a function:
function maxDate(data){
var max = '';
for(var i=0; i<data.length; i++)
if(data[i]>max)
max=data[i];
return max;
}
And then:
var maxT=maxDate(a);
DISCLAIMER: This second method will only work if all the date strings are in the same format, if you have different format dates in your array you will not be able to use this function.
Is the array filled with Date objects?
If so, compare them using them, and sort them using one of the many known algorithms.
If not, recreate the array with Date objects, one for each of them, and do as I said above, by ordering the array.

Regex inside TableSorter parser deletes indices where the same regex works correctly in a separate console function

Using Jquery TableSorter, I am creating a custom parser to sort elapsed time <td>s that contain "'#' year(s) * '#' month(s)". When I use the function
$('.techtable td:nth-child(6)').each(function(){
// console.log($(this));
var that = $(this).text();
var myRegexp = /([\d]+) ([\w]+)(?: ([\d]+) ([\w]+))?/;
var match = myRegexp.exec($(this).text());
console.log(match);
});
from the command line, each index contains an array of length 5, looking like this:
["7 months", "7", "months", undefined, undefined]
to this:
["3 years 3 months", "3", "years", "3", "months"]
depending on whether or not the elapsed time has just a month or year element, and then the other. To parse the text, I use regex to gather each element, and then use JS to test whether there are multiple elements or not, and if 1 element only, then wheher it begins with "y" or "m", and return the number of months, so the parser can sort the <td>s by number of months in integer form.
The parser passes in each element into the function as parameter "s". when i try regex on "s" directly, it is not returning an array of length 5, it is truncating it to 3 (whether or not I am running the line that truncates it if index 3 is typeof 'undefined'). When I use the console to directly use this function:
$('.techtable td:nth-child(6)').each(function(){
// console.log($(this));
var that = $(this).text();
var myRegexp = /([\d]+) ([\w]+)(?: ([\d]+) ([\w]+))?/;
var match = myRegexp.exec($(this).text());
if (typeof match[3] == 'undefined') {match.length = 3;};
console.log(match);
});
the regex returns the arrays properly, with the array truncated if it only has 1 element (year or month). Here is the code for the custom parser:
var myRegexp = /([\d]+) ([\w]+)(?: ([\d]+) ([\w]+))?/;
var match = myRegexp.exec(s);
var order = [];
console.log(match);
if (typeof match[3] == 'undefined') {match.length = 3;};
// 1 element case:
// month
if (match.length = 3) {
if (match[2][0] == "m") {
order.push(match[1]);
}
// year
if (match[2][0] == "y") {
order.push(match[1]*12);
}
// both elements
} else {
order.push(match[1]*12 + match[3]);
}
s = order;
return s;
},
The fiddle is here. The Elapsed parser is second from the bottom of the JS panel. As you can see, since I can't get the months from the array (indices 4 and 5), I can not calculate the months, and thus the sorting only incorporates years, and the months are sorted by their original HTML placement. What am I missing? (I'm learning.... so direction is appreciated more than an fix, but I won't turn it down.)
Yes I realize the JS fiddle is loaded (first part is TableSorter, to maintain functionality for verification(click on headers to sort), but all you need to focus on is the last part of the code (reference the '//Table Sorter dateSorter' to see how a correct parser should look). The section '//Table Sorter elapsedSorter' is where my two attempts are, the first part is the working code I use in the console, and the seconde part is the parser, which is somehow deleting the last two indices in the array, thus loosing the month information to calculate.
Guess I'll have to add Regex, and a personal rating of 1, since I've wasted almost an entire day on this.
if (match.length = 3) {
You meant this?
if (match.length == 3) {
To help you further, when you write conditions with one constant and a variable, you can write them like this instead:
if (3 = match.length) {
This would now cause a JavaScript error instead of silently getting turned into an assignment that always yields true.
In JavaScript, 12 + '4' == '124', so you have to be careful with numbers and the + operator. In languages such as PHP you don't have this problem, because they have an operator for string concatenations ;-)
var myRegexp = /([\d]+) ([\w]+)(?: ([\d]+) ([\w]+))?/;
var match = myRegexp.exec(s);
var order = [];
if (typeof match[3] == 'undefined') {
if (match[2][0] == "m") {
order.push(parseInt(match[1]));
}
// year
if (match[2][0] == "y") {
order.push(parseInt(match[1])*12);
}
// both elements
} else {
order.push(parseInt(match[1])*12 + parseInt(match[3]));
}
s = order;
return s;
Btw use parseInt(x, 10) if you expect fields to have leading zeroes (which would otherwise result in 0 being returned). Thanks fudgey!

jquery Tablesorter - natural sort order?

I've looked through all the answers listed for tablesorter but can't find what I'm looking for.
I have a table which I'm trying to sort, one column contains product model numbers which include both letters and numbers, the problem is that it sorts alphabetically but not with the numbers in their proper order. For example the sorted column comes out like this:
STRB 18,
STRB 19,
STRB 2,
STRB 20,
STRB 23 - you get the idea.
I've gathered that I need this column to sort in natural order, but I've not got any js chops to do this with. If anybody could point me in the right direction I would be very grateful!
taken from some old code of mine
compare = {
natural: function(a, b) {
function prepare(s) {
var q = [];
s.replace(/(\D)|(\d+)/g, function($0, $1, $2) {
q.push($1 ? 1 : 2);
q.push($1 ? $1.charCodeAt(0) : Number($2) + 1)
});
q.push(0);
return q;
}
var aa = prepare(a), bb = prepare(b), i = 0;
do {
if(aa[i] != bb[i])
return aa[i] - bb[i];
} while(aa[i++] > 0);
return 0;
}
}
example of use
test = ['img100b', 'img21', 'img18', 'img100a', 'img1', 'img2']
console.log(test.sort(compare.natural))
The jquery tablesorter plugin offers the ability to write your own parser.
Check out the example and give it a shot!
http://tablesorter.com/docs/example-parsers.html
Not sure what you mean by "natrual" order, but I am guessing you mean that strb 2 would be before strb 18.
The order you have above is correct for strings. if you want to sort by the number portion (assuming all the strb's are always the same) you will have to do a substring to get the number only and then sort the data.
I would also NOT do this in javascript. Let your database work for you and let it handle the order of the data. It will save you a lot of pain.

Categories