vatheck has asked for the wisdom of the Perl Monks concerning the following question:

I have 2 arrays; one contains a list of say light switches, the other contains lines of comma separated values. The lines of comma separated values have $unixtime,$switchname,$state. For our purposes here there are multiple possible states of off and one possible state of on. I want to do something like

foreach (@switches) { find total time switch was off }

I am thinking I want to pattern match down to only one switch (the array is in order based on the unixtime column) then go through one at a time and find a line that doesn't match the /on/ pattern, then continue till I find a line that matches the /on/ pattern. Then substract column one of the on entry from column one from the off entry and add it to a total off time variable. The only other thing is that a switch can be turned on and off multiple times in our window. I am kind of new to perl and it is my first language so any help you can provide with the { find total time switch was off } part would really be appreciated. I know this has to be out there somewhere but I couldn't figure out what to search for so I decided to ask for help instead.
  • Comment on performing operation on multiple items in an array

Replies are listed 'Best First'.
Re: performing operation on multiple items in an array
by ikegami (Patriarch) on Apr 11, 2008 at 04:29 UTC

    Keeping the data in CSV format in memory is odd. One would normally split it when the data is obtained. The cost will be quite noticeable with your current approach since you will have to split each record once for every @switches.

    Whether you pre-split the data or not, I suggest avoiding your current approach and just looping through the data once.

    use strict; use warnings; my %start_time; my %total_time; while (<DATA>) { # Or iterate through your array chomp; my ($time, $name, $state) = split /,/; # Or better: Text::CSV if ($state eq 'on') { if ($start_time{$name}) { warn("$name already on!\n"); next; } $start_time{$name} = $time; } else { if (!$start_time{$name}) { warn("$name not yet on!\n"); next; } $total_time{$name} += $time - delete $start_time{$name}; } } for my $name (sort keys %start_time) { warn("$name never turned off!\n"); } for my $name (sort keys %total_time) { print("$name was on for $total_time{$name} seconds\n"); } __DATA__ 10000,switchA,on 10001,switchB,on 10002,switchC,on 10005,switchA,off 10007,switchA,on 10012,switchC,off 10013,switchB,off 10015,switchA,off 10020,switchE,on 10020,switchD,off 10020,switchF,on 10021,switchF,on

    If you only want the info for some switches, just add a check:

    ... my %switches = map { $_ => 1} @switches; while (<DATA>) { chomp; my ($time, $name, $state) = split /,/; next if !$switches{$name}; ... } ...

      Extending ikegami's (++) to account for vatheck's multi-state lightswitch ("there are multiple possible states of off and one possible state of on") by adding a "switching" state as one of the possible states defined -- quite arbitrarily and sometimes illogically -- as "off":

      #!/usr/bin/perl use strict; use warnings; my %start_time; my %total_time; while (<DATA>) { # Or iterate through your array chomp; my ($time, $name, $state) = split /,/; # Or better: Text::CSV if ($state eq 'on') { if ($start_time{$name}) { warn("$name already on at $time!\n"); next; } $start_time{$name} = $time; } elsif ($state eq 'switching') { if (!$start_time{$name}) { warn("$name changing state at $time!\n"); next; } } else { if (!$start_time{$name}) { warn("$name not yet on at $time\n"); next; } $total_time{$name} += $time - delete $start_time{$name}; } } for my $name (sort keys %start_time) { warn("$name never turned off!\n"); } for my $name (sort keys %total_time) { print("$name was on for $total_time{$name} seconds\n"); } __DATA__ 10000,switchA,on 10001,switchB,on 10002,switchC,on 10005,switchA,off 10007,switchA,on 10012,switchC,off 10013,switchB,off 10015,switchA,off 10020,switchE,on 10020,switchD,off 10020,switchF,on 10021,switchF,on 10099,switchZ,switching 10100,switchZ,on 10101,switchZ,off 10089,switchY,on 10093,switchY,switching 10094,switchY,off

      Produces:

      switchD not yet on at 10020 switchF already on at 10021! switchZ changing state at 10099! switchE never turned off! switchF never turned off! switchA was on for 13 seconds switchB was on for 12 seconds switchC was on for 10 seconds switchY was on for 5 seconds switchZ was on for 1 seconds

        :-) This does make this binary-minded pilgrim wonder though: "Is vatheck controlling a "3-way" bulb?"

      More seriously, depending on the nature of the auditing being done, it might be well to calculate the "on" duration (as of the time of reporting) for those switches which are "never turned off" by subtracting the time of their first appearance in the data, to the last time found for any switch.

        I just wanted to update that this did work for me after a few minor modifications. Mostly just taking out the warnings because the boss only wanted to see the downtimes in a text file with no other noise. thank both of you for the help.