punkish has asked for the wisdom of the Perl Monks concerning the following question:
A poster on the SQLite list asked the following question: given the following data extracted from the db table
The poster wanted to find a first occurrence of "Red," then searching from this record find the previous 4 records that contain "White," then find the Previous 1 that contains "Blue." Now do the same thing but searching "next". In other words, the following result was desired:Field1,Field2,Field3 1.00,Blue,12:00 <----- 40.20,White,12:00 80.30,White,12:00 120.00,White,12:00 126.00,White,12:00 <----- 162.43,White,12:00 <----- 198.86,White,12:00 <----- 235.29,White,12:00 <----- 271.72,Red,03:45 <===== 308.15,White,12:00 <----- 344.58,White,12:00 <----- 381.01,White,12:00 <----- 417.44,White,12:00 <----- 453.87,White,12:00 490.30,White,12:00 526.73,White,12:00 563.16,Red,07:45 599.59,White,12:00 636.02,White,12:00 672.45,White,12:00 708.88,White,12:00 745.31,White,12:00 781.74,White,12:00 818.17,White,12:00 854.60,Blue,12:00 <----- 891.03,White,12:00 963.89,White,12:00 1000.32,White,12:00 1036.75,Red,08:30 1073.18,White,12:00 1109.61,Red,06:00 1146.04,White,12:00 1182.47,White,12:00 1218.90,White,12:00 18516.40,Blue,12:00 1255.33,White,12:00 927.46,White,12:00
1.00,Blue,12:00 126.00,White,12:00 162.43,White,12:00 198.86,White,12:00 235.29,White,12:00 271.72,Red,03:45 308.15,White,12:00 344.58,White,12:00 381.01,White,12:00 417.44,White,12:00 854.60,Blue,12:00
I have marked the desired rows with <----- and <=====
A very complicated SQL was proposed, but this is the kind of thing best accomplished by in the application. I have been recuperating from a post-Thanksgiving flu/fever, so to pass away boredom, I decided to write the following solution in my favorite programming language.
Once you experienced monks get past your laughing at my code (which does work correctly), I would like a lesson into how to approach such class of problems that involve a "window" over an array that spreads on either side of an array element. Of course, more elegant solutions to the above problem are also welcome as they will show me (and other shy monks) a better way.my @res = analyze(); for (@res) { print $_->{f1} . ',' . $_->{f2} . ',' . $_->{f3} . "\n"; } sub analyze { # get the data as an array of hashes just like DBI would return my @data = map { chomp; split /,/; { f1 => $_[0], f2 => $_[1], f3 => $_[2] } } <DATA>; # arrays for holding bits of results my (@res_red, @res_blu, @res_wht, @nxt_res_red, @nxt_res_blu, @nxt_res_wht ); # counters for tracking whether we are before or # after the 'Red' divide my $i = -1; my $j; # array to hold rows *before* the 'Red' divide my @seen; LOOP: for (@data) { if ($i >= 0) { if ($_->{f2} eq 'White') { next LOOP if $j > 3; push @nxt_res_wht, $_; $j++; } if ($_->{f2} eq 'Blue') { push @nxt_res_wht, $_; last LOOP; } } else { if ($_->{f2} eq 'Red') { push @res_red, $_; $i++; my @rev_seen = reverse @seen; my $k; REV_WHITE: for (@rev_seen) { if ($_->{f2} eq 'White') { push @res_wht, $_; $k++ } last REV_WHITE if $k > 3; } REV_BLUE: for (@rev_seen) { if ($_->{f2} eq 'Blue') { push @res_blu, $_; last REV_BLUE; } } push @res_blu, reverse(@res_wht), @res_red; } else { push @seen, $_; } } } push @res_blu, @nxt_res_wht, @nxt_res_red; return @res_blu; } __DATA__ 1.00,Blue,12:00 40.20,White,12:00 80.30,White,12:00 120.00,White,12:00 126.00,White,12:00 162.43,White,12:00 198.86,White,12:00 235.29,White,12:00 271.72,Red,03:45 308.15,White,12:00 344.58,White,12:00 381.01,White,12:00 417.44,White,12:00 453.87,White,12:00 490.30,White,12:00 526.73,White,12:00 563.16,Red,07:45 599.59,White,12:00 636.02,White,12:00 672.45,White,12:00 708.88,White,12:00 745.31,White,12:00 781.74,White,12:00 818.17,White,12:00 854.60,Blue,12:00 891.03,White,12:00 963.89,White,12:00 1000.32,White,12:00 1036.75,Red,08:30 1073.18,White,12:00 1109.61,Red,06:00 1146.04,White,12:00 1182.47,White,12:00 1218.90,White,12:00 18516.40,Blue,12:00 1255.33,White,12:00 927.46,White,12:00
Update: Responding to Limbic~Region's question, forget for the moment possible "Blue" interleaving among the four "Whites" or other anomalies and deformities. That is how the OP on the SQLite stated the problem, so that is how I have reproduced it above.
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Extracting array elements on either side of a match
by GrandFather (Saint) on Dec 01, 2008 at 00:12 UTC | |
by punkish (Priest) on Dec 01, 2008 at 00:21 UTC | |
|
Re: Extracting array elements on either side of a match
by Limbic~Region (Chancellor) on Nov 30, 2008 at 23:52 UTC | |
|
Re: Extracting array elements on either side of a match
by BrowserUk (Patriarch) on Dec 01, 2008 at 00:58 UTC | |
|
Re: Extracting array elements on either side of a match
by hangon (Deacon) on Dec 01, 2008 at 04:41 UTC | |
|
Re: Extracting array elements on either side of a match
by johngg (Canon) on Dec 01, 2008 at 12:41 UTC | |
by BrowserUk (Patriarch) on Dec 01, 2008 at 13:18 UTC |