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

I'm at my wits end. I have a file with multiple lines, data within each line is seperated with a pipe:
husani|10|20 bill|1|3 john|5|2
An array exists for each line, and then an array exists for all of these arrays, via the following code:
open(IN, "<$mailer_data_location"); my @foo = map { [(split(/\|/, $_))] } <IN>; close(IN);
i need to be able to do the following: - search for a specific value, e.g., "bill"
- when value is found, get values of other elements in the array
- modify these elements, and replace array with new values

ive spent a couple of hours searching this site and doing stuff, but it's still not working. please advise? :-)

Replies are listed 'Best First'.
Re: searching array of arrays
by bikeNomad (Priest) on Aug 02, 2001 at 10:16 UTC
    You don't show us what not's working. Just from glancing at the code you show, it looks like you will, in fact, build the data structure you describe (an array of references to arrays with strings in them). Note that the last fields in your arrays will have newlines; you don't say whether that's a problem (chomp is the usual way to deal with this).

    You also don't say why you're building the whole data structure in memory before doing the search (are you?) rather than searching on the fly as the data comes in. But assuming that's what you have to do for some reason, you can just iterate through your array:

    for my $a (@foo) { if ($a->[0] eq 'bill') { $a->[1] += 3; # add 3 to second field $a->[2] .= "!"; # add ! to third field } }

    No reason to replace the array, as you've already replaced the values in it. That is, unless you have references to it elsewhere, in which case you could copy it:
    for my $i (0 .. $#foo) { my $a = $foo[$i]; if ($a->[0] eq 'bill') { my $b = [ @$a ]; # copy a's data to b $b->[1] += 3; # add 3 to second field $b->[2] .= "!"; # add ! to third field $foo[$i] = $b; } }
Maybe use hash of arrays? Re: searching array of arrays
by andye (Curate) on Aug 02, 2001 at 15:12 UTC
    Why not think about using a hash of arrays, rather than an array of arrays? That way you could get at each record quickly and easily.

    You could populate it something like this:

    my %names; while (<>) { chomp; my @line = split /\|/; next unless defined $line[2]; $names{$line[0]} = [@line[1,2]]; }
    and you would get the values for a particular record like this:
    print "values for bill: $names{'bill'}->[0] , $names{'bill'}->[1]";
    and change them like this:
    $names{'bill'}->[0] = 3;
    (NB: Of course, this solution is no good if you have any duplicate names)

    hth, andy.

Re: searching array of arrays
by runrig (Abbot) on Aug 02, 2001 at 10:39 UTC
    You could check out AnyData or DBD::AnyData, but at this point I don't know if it'll make your life easier or not. You ought to be able to get the basics of splitting a field and searching for a value. You are splitting the file ok I think, except the last field still has a newline. If you can describe your problem better, then we can offer better help; otherwise we're just guessing. If you only need to search repeatedly by one unique field (assuming name is unique), and the data will fit in memory, then I'd use a hash array with that field as the key, and let the value just be the line as is (you can save if needed if you store just a scalar and split as needed rather than storing an array). If you need to search by other fields also, take a look at one of the AnyData modules. Or maybe you just want to split into a hash:
    my @fields = qw(name num1 num2); open(IN, "<$mailer_data_location"); while (<IN>) { chomp; my %data; my @data{@foo} = split /\|/, $_; print $_ if $data{name} eq 'Bill'; # Save array of hashes push @AoH, \%data; # Or just save array of values push @array, [ split /\|/, $_ ]; } close(IN);
Re: searching array of arrays
by mattr (Curate) on Aug 02, 2001 at 15:27 UTC
    If you want to do an alphabetic search you can generally do a brute force approach (search every value) or set up an inverted index or tree, which would be really fast to search. An SQL database would probably do this for you so you could just use "LIKE" to search.

    Regexes are also a way to do it; concatenate the whole thing together and then read off the rest of the entry.

    Or a twist on the last suggestion, about using a hash. If you concatenate an incrementing number to the end of your keyword it will work even if you have keys with the same name. Just keep track of how many synonymous keys you have, using another hash (like synonyms{bob}==2 to tell you that $bighash{bob0} and $bighash{bob1} exist.

    But these different strategies do kind of depend on why you are trying to do what you are doing. What is the actual project? Also I'd say this is very basic, homework level stuff (not to be insulting but to say that an inverted index is probably overkill and brute force (no hashes or anything, just the structure you have) is likely best) so probably you are not going to be able to use a database or a module. And you don't need to for this.

    So don't worry about speed. If you can build a structure you can certainly take it apart. Make one subroutine for writing to the structure even if it is just a one liner, and another for reading from it, then treat them as black boxes (after testing them) and your main loop is very short. The best way to stay sane is to break up the problem into small pieces.

Re: searching array of arrays
by greywolf (Priest) on Aug 02, 2001 at 20:03 UTC
    So you have an array of arrays (2 dimensional) built with
    husani|10|20 bill|1|3 john|5|2

    That gives you something like @array[][]. husani is at $array[0][0] and bill is at $array[1][0]. You must loop through the first dimension while checking the appropriate index in the 2nd dimension. When the match is found you can change any element in the 2nd dimension.
    for (my $x = 0; $x < @array; $x+) { if ($array[$x][0] eq 'bill') { $array[$x][1] = 45; } }

    It may not be the slickest way to do it but it will work.