I am using Node's Socket.io
to push data from server to client browser.
On client I use jQuery to populate the returned rows in the DOM.
Below the code snippet I use to populate the data returned by Socket.io.
var OverSpeedAlerts = [];
var TripCancellation = [];
var GeofenceInOutAlerts = [];
var ScheduleOverstay = [];
var UnSchduledOverstay = [];
var SkippedBusStop = [];
var TripDelayAlert = [];
var SkippedUnplannedAlert = [];
var DelayStartEndAlert = [];
var RouteDeviatedAlert = [];
var MultipleBusEntry = [];
Declaring the prototype:
Array.prototype.inArray = function (parer) {
for (var i = 0; i < this.length; i++) {
if (parer(this[i])) return true;
}
return false;
};
// adds an element to the array if it does not already exist using a parer
// function
Array.prototype.pushIfNotExist = function (element, parer) {
if (!this.inArray(parer)) {
this.unshift(element);
}
};
Processing the data recieved from socket:
if (typeof io !== 'undefined') {
var pushServer = io.connect('http://SomeIP:3000');
pushServer.on('entrance', function (data) {
var rows = data.message;
var NumberOfRows = rows.length;
$('#notifications').html(NumberOfRows);
// console.log(rows);
OverSpeedAlerts = [];
TripCancellation = [];
GeofenceInOutAlerts = [];
ScheduleOverstay = [];
UnSchduledOverstay = [];
SkippedBusStop = [];
TripDelayAlert = [];
SkippedUnplannedAlert = [];
DelayStartEndAlert = [];
RouteDeviatedAlert = [];
var MultipleBusEntry = [];
for (var i = 0; i < rows.length; i++) {
if (rows[i].alert_type == 'overspeed') {
OverSpeedAlerts.pushIfNotExist(rows[i], function (e) {
return e.device_id === rows[i].device_id && e.alert_gen_date_time === rows[i].alert_gen_date_time;
});
}
else if (rows[i].alert_type == 'trip_cancellation') {
TripCancellation.pushIfNotExist(rows[i], function (e) {
return e.device_id === rows[i].device_id && e.alert_gen_date_time === rows[i].alert_gen_date_time;
});
}
else if (rows[i].alert_type == 'Geofence-In' || rows[i].alert_type === 'Geofence-Out') {
GeofenceInOutAlerts.pushIfNotExist(rows[i], function (e) {
return e.device_id === rows[i].device_id && e.alert_gen_date_time === rows[i].alert_gen_date_time;
});
}
else if (rows[i].alert_type == 'Scheduled-Overstay') {
ScheduleOverstay.pushIfNotExist(rows[i], function (e) {
return e.device_id === rows[i].device_id && e.alert_gen_date_time === rows[i].alert_gen_date_time;
});
}
else if (rows[i].alert_type == 'Unscheduled-Overstay') {
UnSchduledOverstay.pushIfNotExist(rows[i], function (e) {
return e.device_id === rows[i].device_id && e.alert_gen_date_time === rows[i].alert_gen_date_time;
});
}
else if (rows[i].alert_type == 'Skipped Unplanned' || rows[i].alert_type == 'Skipped-Busstop') {
SkippedBusStop.pushIfNotExist(rows[i], function (e) {
return e.device_id === rows[i].device_id && e.alert_gen_date_time === rows[i].alert_gen_date_time;
});
}
else if (rows[i].alert_type == 'Delay Start' || rows[i].alert_type == 'Delay End') {
TripDelayAlert.pushIfNotExist(rows[i], function (e) {
return e.device_id === rows[i].device_id && e.alert_gen_date_time === rows[i].alert_gen_date_time;
});
}
else if (rows[i].alert_type == 'Route Deviated') {
RouteDeviatedAlert.pushIfNotExist(rows[i], function (e) {
return e.device_id === rows[i].device_id && e.alert_gen_date_time === rows[i].alert_gen_date_time;
});
}
else if (rows[i].alert_type == 'Multiple Bus Entry') {
MultipleBusEntry.pushIfNotExist(rows[i], function (e) {
return e.device_id === rows[i].device_id && e.alert_gen_date_time === rows[i].alert_gen_date_time;
});
}
}
CreateOverSpeedGrid();
CreateTripCancellation();
CreateGeofenceGrid();
CreateScheduleOverstayGrid();
CreateUnSchduledOverstayGrid();
CreateTripDelayGrid();
CreateSkippedBusStop();
CreateRouteDeviationAlert();
CreateMultipleBusEntry();
});
pushServer.on('end', function (socket) {
});
}
One of the above functions are as below. Rest are the similar function filling data in other parts of DOM.
function CreateOverSpeedGrid() {
$('#tabs ul').find('a:contains("Overspeed Alerts")').html('OverSpeed Alerts(' + OverSpeedAlerts.length + ')');
if (OverSpeedAlerts.length != 0) {
$('#notifyOverspeed table').html('');
$('#notifyOverspeed table').append('<tr class="ui-widget-header"> <th> Depot </th> <th> Route </th> <th> Schedule </th> <th> Trip Number </th><th>Trip Direction</th> <th> Alert Summary</th> <th> Alert Details </th> <th> Generated On </th> </tr>'); //new Date([UNIX Timestamp] * 1000);
for (var i = 0; i < OverSpeedAlerts.length; i++) {
$('#notifyOverspeed table').append('<tr> <td>' + OverSpeedAlerts[i].depot_name + '</td> <td>' + OverSpeedAlerts[i].route_name + '</td> <td>' + OverSpeedAlerts[i].schedule_no + '</td> <td>' + OverSpeedAlerts[i].trip_number + ' </td> <td>' + OverSpeedAlerts[i].trip_direction + '</td><td> ' + OverSpeedAlerts[i].alert_sub + ' </td> <td title="' + ConvertToValidTooltip(OverSpeedAlerts[i].alert_msg) + '" style="text-decoration:underline;cursor:pointer;"> ' + "Place mouse pointer to view message" + ' </td> <td>' + new Date(OverSpeedAlerts[i].alert_gen_date_time * 1000) + ' </td> </tr>');
}
}
}
The above code works fine. BUt the problem is that due to so many push messages being received every 10 seconds from socket the browser is not able to process so much data and hangs up.
is there any better way to do it??
I am using Node's Socket.io
to push data from server to client browser.
On client I use jQuery to populate the returned rows in the DOM.
Below the code snippet I use to populate the data returned by Socket.io.
var OverSpeedAlerts = [];
var TripCancellation = [];
var GeofenceInOutAlerts = [];
var ScheduleOverstay = [];
var UnSchduledOverstay = [];
var SkippedBusStop = [];
var TripDelayAlert = [];
var SkippedUnplannedAlert = [];
var DelayStartEndAlert = [];
var RouteDeviatedAlert = [];
var MultipleBusEntry = [];
Declaring the prototype:
Array.prototype.inArray = function (parer) {
for (var i = 0; i < this.length; i++) {
if (parer(this[i])) return true;
}
return false;
};
// adds an element to the array if it does not already exist using a parer
// function
Array.prototype.pushIfNotExist = function (element, parer) {
if (!this.inArray(parer)) {
this.unshift(element);
}
};
Processing the data recieved from socket:
if (typeof io !== 'undefined') {
var pushServer = io.connect('http://SomeIP:3000');
pushServer.on('entrance', function (data) {
var rows = data.message;
var NumberOfRows = rows.length;
$('#notifications').html(NumberOfRows);
// console.log(rows);
OverSpeedAlerts = [];
TripCancellation = [];
GeofenceInOutAlerts = [];
ScheduleOverstay = [];
UnSchduledOverstay = [];
SkippedBusStop = [];
TripDelayAlert = [];
SkippedUnplannedAlert = [];
DelayStartEndAlert = [];
RouteDeviatedAlert = [];
var MultipleBusEntry = [];
for (var i = 0; i < rows.length; i++) {
if (rows[i].alert_type == 'overspeed') {
OverSpeedAlerts.pushIfNotExist(rows[i], function (e) {
return e.device_id === rows[i].device_id && e.alert_gen_date_time === rows[i].alert_gen_date_time;
});
}
else if (rows[i].alert_type == 'trip_cancellation') {
TripCancellation.pushIfNotExist(rows[i], function (e) {
return e.device_id === rows[i].device_id && e.alert_gen_date_time === rows[i].alert_gen_date_time;
});
}
else if (rows[i].alert_type == 'Geofence-In' || rows[i].alert_type === 'Geofence-Out') {
GeofenceInOutAlerts.pushIfNotExist(rows[i], function (e) {
return e.device_id === rows[i].device_id && e.alert_gen_date_time === rows[i].alert_gen_date_time;
});
}
else if (rows[i].alert_type == 'Scheduled-Overstay') {
ScheduleOverstay.pushIfNotExist(rows[i], function (e) {
return e.device_id === rows[i].device_id && e.alert_gen_date_time === rows[i].alert_gen_date_time;
});
}
else if (rows[i].alert_type == 'Unscheduled-Overstay') {
UnSchduledOverstay.pushIfNotExist(rows[i], function (e) {
return e.device_id === rows[i].device_id && e.alert_gen_date_time === rows[i].alert_gen_date_time;
});
}
else if (rows[i].alert_type == 'Skipped Unplanned' || rows[i].alert_type == 'Skipped-Busstop') {
SkippedBusStop.pushIfNotExist(rows[i], function (e) {
return e.device_id === rows[i].device_id && e.alert_gen_date_time === rows[i].alert_gen_date_time;
});
}
else if (rows[i].alert_type == 'Delay Start' || rows[i].alert_type == 'Delay End') {
TripDelayAlert.pushIfNotExist(rows[i], function (e) {
return e.device_id === rows[i].device_id && e.alert_gen_date_time === rows[i].alert_gen_date_time;
});
}
else if (rows[i].alert_type == 'Route Deviated') {
RouteDeviatedAlert.pushIfNotExist(rows[i], function (e) {
return e.device_id === rows[i].device_id && e.alert_gen_date_time === rows[i].alert_gen_date_time;
});
}
else if (rows[i].alert_type == 'Multiple Bus Entry') {
MultipleBusEntry.pushIfNotExist(rows[i], function (e) {
return e.device_id === rows[i].device_id && e.alert_gen_date_time === rows[i].alert_gen_date_time;
});
}
}
CreateOverSpeedGrid();
CreateTripCancellation();
CreateGeofenceGrid();
CreateScheduleOverstayGrid();
CreateUnSchduledOverstayGrid();
CreateTripDelayGrid();
CreateSkippedBusStop();
CreateRouteDeviationAlert();
CreateMultipleBusEntry();
});
pushServer.on('end', function (socket) {
});
}
One of the above functions are as below. Rest are the similar function filling data in other parts of DOM.
function CreateOverSpeedGrid() {
$('#tabs ul').find('a:contains("Overspeed Alerts")').html('OverSpeed Alerts(' + OverSpeedAlerts.length + ')');
if (OverSpeedAlerts.length != 0) {
$('#notifyOverspeed table').html('');
$('#notifyOverspeed table').append('<tr class="ui-widget-header"> <th> Depot </th> <th> Route </th> <th> Schedule </th> <th> Trip Number </th><th>Trip Direction</th> <th> Alert Summary</th> <th> Alert Details </th> <th> Generated On </th> </tr>'); //new Date([UNIX Timestamp] * 1000);
for (var i = 0; i < OverSpeedAlerts.length; i++) {
$('#notifyOverspeed table').append('<tr> <td>' + OverSpeedAlerts[i].depot_name + '</td> <td>' + OverSpeedAlerts[i].route_name + '</td> <td>' + OverSpeedAlerts[i].schedule_no + '</td> <td>' + OverSpeedAlerts[i].trip_number + ' </td> <td>' + OverSpeedAlerts[i].trip_direction + '</td><td> ' + OverSpeedAlerts[i].alert_sub + ' </td> <td title="' + ConvertToValidTooltip(OverSpeedAlerts[i].alert_msg) + '" style="text-decoration:underline;cursor:pointer;"> ' + "Place mouse pointer to view message" + ' </td> <td>' + new Date(OverSpeedAlerts[i].alert_gen_date_time * 1000) + ' </td> </tr>');
}
}
}
The above code works fine. BUt the problem is that due to so many push messages being received every 10 seconds from socket the browser is not able to process so much data and hangs up.
is there any better way to do it??
Share Improve this question asked Jan 23, 2014 at 9:45 writeToBhuwanwriteToBhuwan 3,28111 gold badges42 silver badges67 bronze badges 2- Why dont you just filter the data that you are sending, so that you dont receive so much? – ExxKA Commented Jan 23, 2014 at 10:24
- Array.prototype.inArray = function (item) { return this.indexOf(item) !== -1; }; – Eraden Commented Feb 4, 2014 at 8:23
4 Answers
Reset to default 5 +25I see the following problems in this code:
You update your table by manipulating the document multiple times. It is better for performance to update the DOM in one operation. There's a Google article about this. So something like:
function CreateOverSpeedGrid() { $('#tabs ul').find('a:contains("Overspeed Alerts")').html('OverSpeed Alerts(' + OverSpeedAlerts.length + ')'); if (OverSpeedAlerts.length != 0) { var html = []; html.push('<tr class="ui-widget-header"> <th> Depot </th> <th> Route </th> <th> Schedule </th> <th> Trip Number </th><th>Trip Direction</th> <th> Alert Summary</th> <th> Alert Details </th> <th> Generated On </th> </tr>'); //new Date([UNIX Timestamp] * 1000); for (var i = 0; i < OverSpeedAlerts.length; i++) { html.push('<tr> <td>' + OverSpeedAlerts[i].depot_name + '</td> <td>' + OverSpeedAlerts[i].route_name + '</td> <td>' + OverSpeedAlerts[i].schedule_no + '</td> <td>' + OverSpeedAlerts[i].trip_number + ' </td> <td>' + OverSpeedAlerts[i].trip_direction + '</td><td> ' + OverSpeedAlerts[i].alert_sub + ' </td> <td title="' + ConvertToValidTooltip(OverSpeedAlerts[i].alert_msg) + '" style="text-decoration:underline;cursor:pointer;"> ' + "Place mouse pointer to view message" + ' </td> <td>' + new Date(OverSpeedAlerts[i].alert_gen_date_time * 1000) + ' </td> </tr>'); } // Change the rows in one operation. $('#notifyOverspeed table').html(html.join('')); } }
The
inArray
method you add toArray
has to scan the whole array before determining that an element is not already in the array.Ideally, this filtering would be done at the sending end. This would be best. However, maybe you are using third party data that cannot be filtered at the source, so...
There is a way to do better. If order is important, you could still use arrays to store your objects. Then you could use an object created with
Object.create(null)
to serve as an associative array only to record whether you've seen an object or not. So something like:var OverSpeedAlerts = []; var OverSpeedAlertsSeen = Object.create(null); for (var i = 0; i < rows.length; i++) { var row = rows[i]; var key = row.device_id + row.alert_gen_date_time; if (row.alert_type == 'overspeed' && !OverSpeedAlertsSeen[key]) { OverSpeedAlertsSeen[key] = true; OverSpeedAlerts.push(row); } }
I've used this method more than once but the code above is not tested. Typos or brainos may lurk in there. In this example the key is puted once for all types of alerts. Looking at your code it looked like all your alerts were pared on
device_id
andalert_gen_date_time
so generating the key once for each item in the array is correct.How to generate they key depends on the possible values of
row.device_id
androw.alert_gen_date_time
. You might need a separator to avoid possible ambiguities. For instance, in a case I worked on, I had to bine values where all the letters of the alphabet were valid. So without a separator there would be no way to distinguish"ab" + "cd"
from"a" + "bcd"
from"abc" + "d"
. I used a separator that could not appear in the values to build the key: so"ab@cd"
,"a@bcd"
,"abc@d"
.It would also be possible to use an associative array of associative arrays to do the check but I'm not convinced that it would yield a major speed improvement. It would certainly make the code more plex.
I can think of other changes that could be made to improve speed but I don't think they could provide substantial gains.
Not knowing the details of your app, I'm going to work with the assumption that you need all of that data in the same interface, rather than being able to split it up between, I dunno, tabs or pages or what have you.
If you find it's too much data, presumably your messages don't cycle through so quickly that every 10 seconds it's a whole new batch of data... you likely have longer lived messages sitting in there, being deleted and re-created every 10 seconds.
If you instead changed it so that each update contained only changes, e.g. a new total per list, and then rows to be added and rows to be deleted, you'd likely dramatically improve your performance, and then you could run a full update every, i dunno, 5 minutes (or 15 minutes, or 60 minutes, whatever you feel your app can tolerate) just to make sure you don't get out of sync.
This is almost exactly one of the methods used in video pression, just record the changes from frame to frame, then every so often use a keyframe for error correction.
If you do this, you could eliminate your pushifnotexists
step, just loop directly through your response data and update the table all in the same step.
This is not entirely finished (some repetitive typing is missing at the dom updates) I have eliminated your need for the array prototypes, and made a lookupCache for items allready in the list
The updatedom method is an altered version of the one you had, and youll have to re-write the others you used.
At most parts of the code i added some explination on whats going on and why. The dom update part can still be made faster, but i expect only rending the new part and updating the dom as a whole will be sufficient, while still making the code understandable and keeping it (relatively) smallish :D, its quite a lot...
There is one confusing thing in the code you provided tho. Initailly you declare the lists (var OverSpeedAlerts etc.) but then inside the processing method for the data received you re-set them to empty array's. Was it: 1. your intention to keep the previous dom elements but try to optimize 2. your intention to replace everything with the new data 3. add only the new data to the existing data (which is what this code does)
Either case the ments in the code will explain where and how you can optimize your existing code if you wish to do so. (most of it will be the single dom update tho) but the filtering will help especially on big lists.
var io /*some lib*/, pushServer, alertTypes, alterTypesMapping, $notifications, lookupCache;
//i use the -Length fields to store the "previous" length, so i know if the dom needs updating at all
// and what part is new, no need to re-render perfectly valid html
alertTypes = {
OverSpeedAlerts: [],
OverSpeedAlertsLength: 0,
TripCancellation: [],
TripCancellationLength: 0,
GeofenceInOutAlerts: [],
GeofenceInOutAlertsLength: 0,
ScheduleOverstay: [],
ScheduleOverstayLength: 0,
UnSchduledOverstay: [], //scheduled? sorry ide with spelling check
UnSchduledOverstayLength: 0,
SkippedBusStop: [],
SkippedBusStopLength: 0,
TripDelayAlert: [],
TripDelayAlertLength: 0,
SkippedUnplannedAlert: [],
SkippedUnplannedAlertLength: 0,
DelayStartEndAlert: [],
DelayStartEndAlertLength: 0,
RouteDeviatedAlert: [],
RouteDeviatedAlertLength: 0
};
//mapping from types to their lists (some types map to the same list)
alterTypesMapping = {
'overspeed': 'OverSpeedAlerts',
'trip_cancellation': 'TripCancellation',
'Geofence-In': 'GeofenceInOutAlerts',
'Geofence-Out': 'GeofenceInOutAlerts',
'Scheduled-Overstay': 'ScheduleOverstay',
'Unscheduled-Overstay': 'UnSchduledOverstay',
'Skipped Unplanned': 'SkippedBusStop',
'Delay Start': 'TripDelayAlert',
'Delay End': 'TripDelayAlert',
'Route Deviated': 'RouteDeviatedAlert',
'Multiple Bus Entry': 'MultipleBusEntry'
};
//cache dom lookup
$notifications = $('#notifications');
//we serialize the relevant message parts into an unique id, used for de-duping
//<id> => <alert_type>|<device_id>|<alert_gen_date_time>
lookupCache = {};
function process_data (data) {
var i, l, rows, id;
rows = data.message;
l = rows.length;
//update dom row count
$notification.html(l);
for (i=0; i<l; ++i) { //caching length in l, ++i is faster than i++
id = rows[i].alert_type + '|' + rows[i].device_id + '|' + rows[i].alert_gen_date_time;
if (!lookupCache[id]) {
lookupCache[id] = 1; //set it to truthy so next time around its there
//not in cache push it to the relevant list
//you used unshift here, that's essentially moving all other elements in the list one spot and
//adding the new one at index 0 (speed O(n) eg increases with more elements in the list)
//instead you can push the new element to the end, (speed O(1) constant speed)
// and when iterating the list doing that in reverse
alertTypes[alterTypesMapping[rows[i].alert_type]].push(rows[i]);
}
}
updateDom();
}
function updateDom () {
var keys, i, l, html;
//here we check all length fields in the alertTypes and see if the actual list length
//is greater than their -Length
//if so we update the relevant dom
keys = Object.keys(alertTypes);
for (i=0, l=keys.length; i<l; ++i) {
//skip the -Length keys
if (keys[i].match('Length')) {
continue;
}
//please add a data-type="<type>" to the a's, so much better to lookup by attribute instead of text matching content
$('#tabs ul a[data-type="' + keys[i] + '"]').html(keys[i] + '(' + alertTypes[keys[i] + 'Length'] + ')');
//since well only update the dom, i presume at this point there is a dom with the table with headers
//(use thead and th for this please)
//(and use tbody for a table's body)
//now we iterate the new elements (from list.length back to key-Length)
//j starts at the length of the list, and ends at m, the previous length
//counting backwards
html = [];
for (j=alertTypes[keys[i]].length, m=alertTypes[keys[i] + 'Length']; j>m; --j) {
//array join is almost always faster than string concatenation
//since strings are not mutable in js (eg. you create a new string every +)
html.push([
'<tr>',
'<td>',
alertTypes[keys[i]].depot_name,
'</td>',
'<td>',
alertTypes[keys[i]].route_name,
'</td>',
'<td>',
alertTypes[keys[i]].schedule_no,
'</td>',
'<td>',
alertTypes[keys[i]].trip_number,
'</td>',
'<td>',
alertTypes[keys[i]].trip_direction,
'</td>',
'<td>',
alertTypes[keys[i]].alert_sub,
'</td>',
'<td ',
'title="',
ConvertToValidTooltip(alertTypes[keys[i]].alert_msg),
'" style="text-decoration:underline;cursor:pointer;">Place mouse pointer to view message',
'</td>',
'<td>',
new Date(OverSpeedAlerts[i].alert_gen_date_time * 1000),
'</td>',
'</tr>'
].join(''));
}
//and finally we update the key-Length so next time well only add what is newer than what we are about to add
alertTypes[kesy[i] + 'Length'] = alertTypes[keys[i]].length;
//get the dom element we have to update
$('#' + keys[i] + ' tbody').prepend(html.join(''));
}
}
if (io !== undefined) { //no need for typeof when checking undefined, check undefined directly with equality (eg. undefined === undefined)
pushServer = io.connect('http://SomeIP:3000');
pushServer.on('entrance', process_data);
}
The costly operations are DOM manipulations.
You can improve themby providing your DOM string in one shot, without invoking append multiple time. The way you select your DOM nodes is also very important.
An example:
function CreateOverSpeedGrid() {
// Use an ID if possible. It's the fastest way to select DOM nodes. After you can use css classes, or dom custom attributes (data-***). Using contains is very slow.
$('#overspeed_alerts').html('OverSpeed Alerts(' + OverSpeedAlerts.length + ')');
if (OverSpeedAlerts.length != 0) {
// Fast HTML string: using an array is better than concatenating multiple strings.
var content = ['<tr class="ui-widget-header"> <th> Depot </th> <th> Route </th> <th> Schedule </th> <th> Trip Number </th><th>Trip Direction</th> <th> Alert Summary</th> <th> Alert Details </th> <th> Generated On </th> </tr>'];
for (var i = 0; i < OverSpeedAlerts.length; i++) {
// optimize by accessing your item in array only once.
var alert = OverSpeedAlerts[i];
content.push('<tr> <td>',
alert.depot_name,
'</td> <td>',
alert.route_name,
'</td> <td>',
alert.schedule_no,
'</td> <td>',
alert.trip_number,
'</td> <td>,
alert.trip_direction,
'</td><td> ',
alert.alert_sub,
'</td><td title="',
ConvertToValidTooltip(alert.alert_msg),
'" style="text-decoration:underline;cursor:pointer;">Place mouse pointer to view message </td><td>',
new Date(alert.alert_gen_date_time * 1000),
'</td> </tr>'];
}
// Do not select multplie time your table node: it better to store it in a variable.
$('#notifyOverspeed table').html(content.join(''));
}
}
Only two DOM access in this solution, instead of N+4 in your first implementation (N is the length of OverSpeedAlerts array.
You can also improve the pushIfNotExist
method, which is also very time-consuming in my opinion.
I suggest to use a hash in addition to your array, and to use as hash key which is a join between "alert_gen_date_time"
and "device_id"
:
// use hash in addition to your array
OverSpeedAlerts = [];
var existingOverSpeedAlerts = {};
for (var i = 0; i < rows.length; i++) {
// optimize access to row
var row = rows[i];
if (row.alert_type == 'overspeed') {
// join device_id and alert_gen_date_time
var key = row.device_id + '_' + row.alert_gen_date_time;
// existence check ! very efficient.
if (!(key in existingOverSpeedAlerts )) {
// does not exist yet: adds it, and update your hash.
existingOverSpeedAlerts[key] = row;
OverSpeedAlerts.push(row);
}
}
...
With this, there is no more need to check if the alert is already in the array, because the language test it for you. No need for inArray
and pushIfNotexist
anymore !
You can also factorize your code by dynamically choosing your hash, depending on the alert_type. It will not make your code fast, but just more readable (which is also valuable !)
Something like :
if (typeof io !== 'undefined') {
var pushServer = io.connect('http://SomeIP:3000');
pushServer.on('entrance', function (data) {
var rows = data.message;
var NumberOfRows = rows.length;
$('#notifications').html(NumberOfRows);
var alerts = {};
var keys = {};
// reuse NumberOfRows here
for (var i = 0; i < NumberOfRows ; i++) {
// optimize access to row
var row = rows[i];
var type = row.alert_type;
// add here specificities relative type aggregation
if (type === 'Geofence-In' || type === 'Geofence-Out') {
type = 'Geofence-Inout';
} else if (type === 'Skipped Unplanned' || type === 'Skipped-Busstop') {
type = 'SkippedBusStop';
} else if (type === 'Delay Start' || type === 'Delay End') {
type = 'TripDelayAlert';
}
// first alert of a kind !
if (!(type in alerts)) {
// init your alert specific array and key hash
alerts[row.alert_type] = [];
keys[row.alert_type] = {};
}
// join device_id and alert_gen_date_time
var key = row.device_id + '_' + row.alert_gen_date_time;
// existence check ! very efficient.
if (!(key in keys[row.alert_type])) {
// does not exist yet: adds it, and update your hash.
keys[row.alert_type][key] = row;
alerts[row.alert_type].push(row);
}
}
// And now displayal
DisplayAlerts(alerts)
}
...
function DisplayAlerts(alerts) {
for (var key in alerts) {
var array = alerts[key];
// My hypothesis is that rendering of a given alert type is inside a node with the kind as css ID.
$('#'+key+' .caption').html('Alerts(' + array.length + ')');
if (array.length != 0) {
// Fast HTML string: using an array is better than concatenating multiple strings.
var content = ['<tr class="ui-widget-header"> <th> Depot </th> <th> Route </th> <th> Schedule </th> <th> Trip Number </th><th>Trip Direction</th> <th> Alert Summary</th> <th> Alert Details </th> <th> Generated On </th> </tr>'];
for (var i = 0; i < OverSpeedAlerts.length; i++) {
// optimize by accessing your item in array only once.
var alert = array[i];
content.push('<tr> <td>',
alert.depot_name,
'</td> <td>',
alert.route_name,
'</td> <td>',
alert.schedule_no,
'</td> <td>',
alert.trip_number,
'</td> <td>,
alert.trip_direction,
'</td><td> ',
alert.alert_sub,
'</td><td title="',
ConvertToValidTooltip(alert.alert_msg),
'" style="text-decoration:underline;cursor:pointer;">Place mouse pointer to view message </td><td>',
new Date(alert.alert_gen_date_time * 1000),
'</td> </tr>'];
}
// Do not select multplie time your table node: it better to store it in a variable.
$('#' + key + ' table').html(content.join(''));
}
}
Happy coding !