Reputation: 1436
I have a log analysis script to populate a complex visualisation.
Picture an array (called, rather unoriginally, 'log') of Activity objects, each of which is in the form:
{
name:foo,
activities:[
{time:t, action:a},
{time:t, action:a},
{time:t, action:a},
...
]
}
There will be up to 75 activity objects in the array, each containing an array of 400-600 actions (one timeslot every 5 minutes since midnight the previous day).
Given a known activity name (foo above) and a time that will already exist in the activities array, I need to update the associated action.
Each name will be unique and each time is in ascending order in the array in exact 5 minutes increments.
As I have to do this a little over 1000 times every time the graph is updated (so an average of 1000 values to update and 1000*500*60 points to plot), performance is a fairly key issue...
Looping in jq is far more efficient than anything I could write so, at the moment, I have
n = "foo";
t = new Date(y,mm,d,h,m).toLocaleString() // matches a time stamp in the log
$.grep($.grep(log, function(n, i)
{
return (n.name == n)
}
)[0].activities, function(n, i)
{
return (n.time == t)
}
)[0].action = "bar";
That seems to be working, but it's taken me so long and I've had so many arguments with myself that I'm not confident.
Am I missing a better way?
Upvotes: 1
Views: 182
Reputation: 9336
Seems like you want the first matched activity of the first matched log.
In that case, you should break the loop after the first match is found. You can do this with .some()
.
n = "foo";
t = new Date(y,mm,d,h,m).toLocaleString() // matches a time stamp in the log
log.some(function(ob, i) {
if (ob.name == n) {
ob.activities.some(function(ob2, i) {
if (ob2.time == t) {
ob2.action = "bar";
return true;
}
});
return true;
}
});
Also, your n
parameter was shadowing your n
variable, so I changed the param to ob
.
But for
loops will generally be quite a bit faster than functional methods.
n = "foo";
t = new Date(y,mm,d,h,m).toLocaleString() // matches a time stamp in the log
for (var i = 0; i < log.length; i++) {
var ob = log[i];
if (ob.name == n) {
for (var j = 0; j < ob.activities.length; j++) {
var ob2 = ob.activities[j];
if (ob2.time == t) {
ob2.action = "bar";
break;
}
}
break;
}
}
If you decide that you should keep the outer loop going if there's no match found on the inner loop, change the code to one of these:
n = "foo";
t = new Date(y,mm,d,h,m).toLocaleString() // matches a time stamp in the log
log.some(function(ob, i) {
if (ob.name == n) {
return ob.activities.some(function(ob2, i) {
if (ob2.time == t) {
ob2.action = "bar";
return true;
}
});
}
});
n = "foo";
t = new Date(y,mm,d,h,m).toLocaleString() // matches a time stamp in the log
OUTER:
for (var i = 0; i < log.length; i++) {
var ob = log[i];
if (ob.name == n) {
for (var j = 0; j < ob.activities.length; j++) {
var ob2 = ob.activities[j];
if (ob2.time == t) {
ob2.action = "bar";
break OUTER;
}
}
}
}
Upvotes: 1
Reputation: 4164
I wont give you a better loop method
for your problem, as any loop you come up with will relatively be no better than the last.
If you truly want a solution that will enhance performance, you should think about rearranging your object entirely. If every name
of each log and time
of each activities array is unique, you can change your object setup to have those values as the key of each subobject
.
Using this method, you'll just be doing a key look up, no loop needed.
New LOG Object
var log =
{
unique_name : {
"activities" : {
time_1 : action_1,
time_2 : action_2,
time_3 : action_3,
etc...
}
},
unique_name_2 : {
"activities" : {
etc...
}
}
}
Now with var u_name = "foo";
and var t = "some time";
you can simply do...
log[u_name][t] = "some action";
Hope this helps!
Upvotes: 2