CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Intermediate:
splicing problem

 



parham_m_s
journeyman

Jan 4, 2001, 3:19 PM

Post #1 of 3 (244 views)
splicing problem Can't Post

i have a flat database which looks something like this:
21432|some text
23453|some text
35234|some text
93845|some text
(id number)|(message)
and so on

my problem is that i use checkboxes to select id numbers (several at a time). If a checked id number is present, that id number will be deleted from the record along with its message.
Here's the coding i use:


Code
foreach $data (@data) { 
($id,$message) = split(/\|/, $data);
$data_count++;
if ($FORM{$id} eq 1) {
$data_count--;
splice(@data, $data_count, 1);
}
}

open (DATABASE, ">$dir") or &message("file error");
flock (DATABASE, 2);
print DATABASE @data;
flock (DATABASE, 8);
close(DATABASE);

what ends up happening when i select multiple checkboxes is that the wrong database records get deleted. What i wanna know is if i can splice several records at a time, after pushing array numbers to a new array. I don't think that made sense, but hope someone can help =). Or if anyone has a completely different approach, i'm open to ideas.



rGeoffrey
User / Moderator

Jan 4, 2001, 6:13 PM

Post #2 of 3 (239 views)
Re: splicing problem [In reply to] Can't Post

Here is a solution that works. I assume that the datafile is not so huge that slurping the whole thing in is too expensive and that we will read and write to the same file.


Code
#--------------------------------- 
#contents of my test 'inputdata' file
21432|some text a
23453|some text b
35234|some text c
93845|some text d

#---------------------------------
#variables that need setting (with my test data)
my %FORM = map { $_ => 1 } qw (21432 35234);
my $dir = 'inputdata';

#---------------------------------

open (LOCK, ">$dir.lck") or &message("semaphore error, could not open");
flock (LOCK, 2) or &message("semaphore error, could not flock");

open (DATABASE, $dir) or &message("file error, read $!");
my @data = <DATABASE>;
close (DATABASE);

@data = map { (exists $FORM{(split(/\|/, $_))[0]}) ? () : $_ } @data;

open (DATABASE, ">$dir") or &message("file error, write $!");
print DATABASE @data;
close (DATABASE);

close (LOCK);

#---------------------------------

Because we want to read and write the same file we need to flock a different file (a semaphore file) that can remain locked after we finish reading and get reset for writing. Also closing a flocked file will release the lock so there is no reason to explicitly release the flock.

But the real answer is all on this line...


Code
@data = map { (exists $FORM{(split(/\|/, $_))[0]}) ? () : $_ } @data;

Starting with @data we do a split and see if the id from the first position exists in the %FORM hash. If it exists, then don't pass anything through " ? () ", otherwise send the whole line through " : $_ ". The new @data now is missing any lines that have id's that exist in %FORM.

The last time I gave an answer using this basic pattern...

Code
map { (some test) ? $_ : () }

japhy pointed out that it was exactly what grep does. I think this one is better off with a map, but I could be wrong. Finding a grep solution for this line is left as an exercise for the reader.




japhy
Enthusiast

Jan 5, 2001, 7:55 AM

Post #3 of 3 (232 views)
Re: splicing problem [In reply to] Can't Post

It is very dangerous to remove elements from an array while you are iterating over it. If you do this, you must do it safely and correctly. After investigation, I have come across a solution (although I'd really use rGeoffrey's method).


Code
@a = (1..9); 
for (@a) {
$x++;
if ($_ % 3 == 0) {
splice @a, --$x, 1;
print "@a\n";
}
}

This code produces the undesired results of:

1 2 4 5 6 7 8 9
1 2 4 6 7 8 9
1 2 4 6 8 9


As you can see, the first record is deleted fine, the second record is deleted ONE too early, and the third record is deleted TWO too early. This number is the number of records already deleted. To accomodate for this, I will store the size of the array in a variable, and then calculate the difference each time I splice():


Code
$s = @a = (1..9); 
for (@a) {
$x++;
if ($_ % 3 == 0) {
splice @a, --$x + ($s - @a), 1;
print "@a\n";
}
}

This gives me the expected output of:

1 2 4 5 6 7 8 9
1 2 4 5 7 8 9
1 2 4 5 7 8


Jeff "japhy" Pinyan -- accomplished hacker, teacher, lecturer, and author

 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives