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


Hello Monks,


Ok so for some reason I'm having trouble figuring this out and hopefully someone can help me. I can think of two different ways to go about this.

First Way:
I have a 2D-Array which the rows can vary in length but the columns are always the same.
i.e. ---> @AoA[$x][$y]

I also have a Sub-Routine in my code which goes through the array and if a certain username is found in a row, I save a reference number to that line in another simple one-dimensional array. So if I find the username I want, I save $x to an array called @excluded.

So for example:
@AoA has the following (BTW the pipes "|" are not actually part of the array)
922337219576856 |rgiles |5005444 |156 |pts/220 |9:00:44 922337219576856 |mrandall |3309650 |35 |none |9:10:44 922337219576856 |mmartin |4565899 |122 |pts/101 |9:15:44 59761456123786 |rkelly |5555555 |999 |pts/900 |9:00:00 59761456123786 |mvick |1234567 |886 |none |9:20:00 59761456123786 |jrussel |7654321 |456 |tty/101 |10:00:00 86555522211 |cklien |5151515 |000 |tty/100 |10:00:00 86555522211 |mmartin |1234567 |987 |none |11:00:00

The username I want is "mmartin".
So the array @excluded would contain:
@excluded = (2, 7)

So the main question is this:
How would I loop through the original 2D-Array (@AoA) and assign each element exactly how it is to another 2D-Array and if the current line is equal to a value in @excluded I want to skip that line and continue on to the next?

I know I could probably just loop through and just use the name to exclude the line but I'm adding to existing code. And the Array holding the references to the row line numbers already is being used in the code so I thought I should use that.

Here's what I tried:
Code is VERY rough tried so many different things not even sure where I left off but here's what I tried.
sub killSession { my $SysPerf; my $recordRef = 0; #Holds the names of users from SysPerf. my @SPUsers = ("bill", "mike", "kevin", "mmartin", "sp"); #Loop through all the records (a line at a time), and search for +SPUsers in that "full" record. for (my $x = 0; $x <= $#records; $x++) { for (my $y = 0; $y <= $#SPUsers; $y++) { if ("@{ $AoA[$x] }" =~ "$SPUsers[$y]" && $AoA[$x][0] eq 'O +WNER') { $SPrecords[$recordRef] = $x; $SysPerf .= "@{ $AoA[$x] }\n"; $recordRef++; } } } #IF SPrecords is not empty, then there are SP user's who own recor +ds. Do not kill these records. #IF they exist then we want to remove them from the array my $num = 0; if (@SPrecords) { for (my $x = 0; $x <= $#records; $x++) { if ($x ne @SPrecords) { for (my $y = 0; $y <= 13; $y++) { $AoA[$x][$y] = $recordsToDel[$num][$y]; print "$recordsToDel[$num][$y]"; } $num++; } } } }


Second Way:

This way I found to be much harder.
And that is to use the original array and splice/delete out the lines containing the usernames I want. But I couldn't figure out how to delete a row and then shift the rest up.


Any suggestions would be greatly appreciated.

Thanks in Advance,
Matt

.

Replies are listed 'Best First'.
Re: Help to assign a 2D-Array to another while excluding specific rows.
by SuicideJunkie (Vicar) on Sep 13, 2011 at 21:09 UTC

    It sounds like you might want to use grep:

    my @array = (0,1,2,3,4,5); @array = grep {$_ % 3} @array; #replace $_%3 with whatever condition +applies. print @array;
    which returns "1245"

    You can chop one element out with a splice:

    my @array = (0,1,2,3,4,5); my $cutme = <>; @array = @array[0..$cutme-1, $cutme+1..$#array]; print @array;

    Entering "2" gives "01345", "0" gives "12345", and so on. Of course, doing so changes the indices of some of the remaining elements, so be careful with this one.

Re: Help to assign a 2D-Array to another while excluding specific rows.
by kennethk (Abbot) on Sep 13, 2011 at 21:04 UTC
    Rather than trying to keep track of indices, it probably makes more sense to just use a hash. Something with logic similar to:
    #!/usr/bin/perl -w use strict; my @records = <DATA>; my @AoA = map [split /\s*\|\s*/], @records; my %SPUsers = map {$_=>1} ("bill", "mike", "kevin", "mmartin", "sp"); my %excluded; for my $row (@AoA) { if ($SPUsers{$row->[1]}) { $excluded{$row}++; } } for my $row (@AoA) { next if $excluded{$row}; print join "|", @$row; } __DATA__ 922337219576856 |rgiles |5005444 |156 |pts/220 |9:00:44 922337219576856 |mrandall |3309650 |35 |none |9:10:44 922337219576856 |mmartin |4565899 |122 |pts/101 |9:15:44 59761456123786 |rkelly |5555555 |999 |pts/900 |9:00:00 59761456123786 |mvick |1234567 |886 |none |9:20:00 59761456123786 |jrussel |7654321 |456 |tty/101 |10:00:00 86555522211 |cklien |5151515 |000 |tty/100 |10:00:00 86555522211 |mmartin |1234567 |987 |none |11:00:00
Re: Help to assign a 2D-Array to another while excluding specific rows.
by johngg (Canon) on Sep 13, 2011 at 23:16 UTC
    that is to use the original array and splice/delete out the lines containing the usernames I want. But I couldn't figure out how to delete a row and then shift the rest up

    I think splice and unshift are the tools for this job but the trick is to work your way forward from the end of the array so that you don't get indices tangled.

    use strict; use warnings; use Data::Dumper; my @records = ( [ qw{ 922337219576856 rgiles 5005444 156 pts/220 9:00:44 } ], [ qw{ 922337219576856 mrandall 3309650 35 none 9:10:44 } ], [ qw{ 922337219576856 mmartin 4565899 122 pts/101 9:15:44 } ], [ qw{ 59761456123786 rkelly 5555555 999 pts/900 9:00:00 } ], [ qw{ 59761456123786 mvick 1234567 886 none 9:20:00 } ], [ qw{ 59761456123786 jrussel 7654321 456 tty/101 10:00:00 } ], [ qw{ 86555522211 cklien 5151515 000 tty/100 10:00:00 } ], [ qw{ 86555522211 mmartin 1234567 987 none 11:00:00 } ], ); my @excludeThese = qw{ rgiles mrandall rkelly mvick jrussel cklien }; my $rxExclude = do { local $" = q{|}; qr{(?:@excludeThese)}; }; my @excluded; foreach my $idx ( reverse 0 .. $#records ) { unshift @excluded, splice @records, $idx, 1 if $records[ $idx ]->[ 1 ] =~ $rxExclude; } print Data::Dumper->Dumpxs( [ \ @records, \ @excluded ], [ qw{ *records *excluded } ] );

    The output.

    @records = ( [ '922337219576856', 'mmartin', '4565899', '122', 'pts/101', '9:15:44' ], [ '86555522211', 'mmartin', '1234567', '987', 'none', '11:00:00' ] ); @excluded = ( [ '922337219576856', 'rgiles', '5005444', '156', 'pts/220', '9:00:44' ], [ '922337219576856', 'mrandall', '3309650', '35', 'none', '9:10:44' ], [ '59761456123786', 'rkelly', '5555555', '999', 'pts/900', '9:00:00' ], [ '59761456123786', 'mvick', '1234567', '886', 'none', '9:20:00' ], [ '59761456123786', 'jrussel', '7654321', '456', 'tty/101', '10:00:00' ], [ '86555522211', 'cklien', '5151515', '000', 'tty/100', '10:00:00' ] );

    I hope this is helpful.

    Cheers,

    JohnGG

Re: Help to assign a 2D-Array to another while excluding specific rows.
by scorpio17 (Canon) on Sep 13, 2011 at 21:32 UTC
    use strict; use Data::Dumper; # read records my @row = <DATA>; my @data = map [split /\s*\|\s*/], @row; print Dumper(\@data), "\n\n"; my @CopyOfData; my @excluded; for my $row_index (0..$#data) { # find rows to skip if ( $data[$row_index][1] =~ /mmartin/ ) { push(@excluded, $row_index); next; } # copy everything else push(@CopyOfData, $data[$row_index]); } print Dumper(\@CopyOfData), "\n"; print Dumper(\@excluded), "\n"; # 2, 7 __DATA__ 922337219576856 |rgiles |5005444 |156 |pts/220 |9:00:44 922337219576856 |mrandall |3309650 |35 |none |9:10:44 922337219576856 |mmartin |4565899 |122 |pts/101 |9:15:44 59761456123786 |rkelly |5555555 |999 |pts/900 |9:00:00 59761456123786 |mvick |1234567 |886 |none |9:20:00 59761456123786 |jrussel |7654321 |456 |tty/101 |10:00:00 86555522211 |cklien |5151515 |000 |tty/100 |10:00:00 86555522211 |mmartin |1234567 |987 |none |11:00:00
Re: Help to assign a 2D-Array to another while excluding specific rows.
by BrowserUk (Patriarch) on Sep 13, 2011 at 21:40 UTC

    Maybe I misunderstand you, but you seem to be making hard work of something quite simple?:

    pp \@aoa;; [ ["922337219576856 ", "rgiles ", "5005444 ", "156 ", "pts/220 ", "9 +:00:44"], ["922337219576856 ", "mrandall ", "3309650 ", "35 ", "none ", "9 +:10:44"], ["922337219576856 ", "mmartin ", "4565899 ", "122 ", "pts/101 ", "9 +:15:44"], ["59761456123786 ", "rkelly ", "5555555 ", "999 ", "pts/900 ", "9 +:00:00"], ["59761456123786 ", "mvick ", "1234567 ", "886 ", "none ", "9 +:20:00"], ["59761456123786 ", "jrussel ", "7654321 ", "456 ", "tty/101 ", "1 +0:00:00"], ["86555522211 ", "cklien ", "5151515 ", "000 ", "tty/100 ", "1 +0:00:00"], ["86555522211 ", "mmartin ", "1234567 ", "987 ", "none ", "1 +1:00:00"], ] $excluded = 'mmartin';; @new = grep $_->[1] !~ $excluded, @aoa;; pp\@new;; [ ["922337219576856 ", "rgiles ", "5005444 ", "156 ", "pts/220 ", "9 +:00:44"], ["922337219576856 ", "mrandall ", "3309650 ", "35 ", "none ", "9 +:10:44"], ["59761456123786 ", "rkelly ", "5555555 ", "999 ", "pts/900 ", "9 +:00:00"], ["59761456123786 ", "mvick ", "1234567 ", "886 ", "none ", "9 +:20:00"], ["59761456123786 ", "jrussel ", "7654321 ", "456 ", "tty/101 ", "1 +0:00:00"], ["86555522211 ", "cklien ", "5151515 ", "000 ", "tty/100 ", "1 +0:00:00"], ]

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Help to assign a 2D-Array to another while excluding specific rows.
by mmartin (Monk) on Sep 14, 2011 at 13:54 UTC

    Hey Guys, Thank you for ALL the replies!

    Great suggestions from everyone. It seems that most of you think a hash is the way to go, and I'm sure your absolutely right, but I was trying not to have to use hashes. Because I'm fairly new to perl and I was trying to keep it simple.

    I do like the idea of using a new array and then using grep to find anything not equal to the given usernames, and assign the elements to the NEW array that way.

    I will give your suggestions a try and see what works best for me.

    Thank you all again I really appreciate the time you all took to respond.
    I will report back with my results or more questions.

    One question at the moment for SuicideJunkie.
    You said to use splice to chop out an element. Is it possible to use that to completely remove an element and not just replace it with, say an empty element?
    For example: If I have an array equal to the data given above in the OP, with elements from 0 to 7, and say elements 0 and 5 have the usernames I want to exclude, and I use splice as you suggested will my array now be (undef, 1, 2, 3, 4, undef, 6, 7)? Or will it remove them and now the array is (0, 1, 2, 3, 4, 5)?


    Thanks again, Matt

    .
Re: Help to assign a 2D-Array to another while excluding specific rows.
by mmartin (Monk) on Sep 14, 2011 at 16:43 UTC

    Hey guys I've been messing around with this some more, and I think I found a sufficient way to do this.
    I don't know why this works now because I feel like I tried this already and it didn't work then.


    Data in Array:
    OWNER | 922337219576856 |rgiles |5005444 |156 |pts/220 |9:00:44 WAITING | 922337219576856 |mrandall |3309650 |35 |none |9:10:44 OWNER | 922337219576856 |mmartin |4565899 |122 |pts/101 |9:15:44 WAITING | 59761456123786 |rkelly |5555555 |999 |pts/900 |9:00:00 WAITING | 59761456123786 |mvick |1234567 |886 |none |9:20:00 WAITING | 59761456123786 |jrussel |7654321 |456 |tty/101 |10:00:00 OWNER | 86555522211 |joe |5151515 |000 |tty/100 |10:00:00 WAITING | 86555522211 |mmartin |1234567 |987 |none |11:00:00

    CODE:
    #Holds the names of users from SysPerf. my @SPUsers = ("mike", "john", "joe", "mmartin", "sp"); #Loop through all the records (a line at a time), and search for +matching users for (my $x = 0; $x <= $#records; $x++) { for (my $y = 0; $y <= $#SPUsers; $y++) { if ("@{ $AoA[$x] }" =~ "$SPUsers[$y]" && $AoA[$x][0] eq 'O +WNER') { splice @AoA,$x,1; } } } for (my $x = 0; $x <= $#AoA; $x++) { print "@{ $AoA[$x] }\n"; } print "Length of \$AoA = $#AoA\n";


    ____OUTPUT____
    OWNER | 922337219576856 |rgiles |5005444 |156 |pts/220 |9:00:44 WAITING | 922337219576856 |mrandall |3309650 |35 |none |9:10:44 WAITING | 59761456123786 |rkelly |5555555 |999 |pts/900 |9:00:00 WAITING | 59761456123786 |mvick |1234567 |886 |none |9:20:00 WAITING | 59761456123786 |jrussel |7654321 |456 |tty/101 |10:00:00 OWNER | 86555522211 |joe |5151515 |000 |tty/100 |10:00:00 Length of $AoA = 5
      for (my $x = 0; $x <= $#AoA; $x++) { print "@{ $AoA[$x] }\n"; }

      Consider replacing the above with this:

      print "@$_\n" for @AoA;

      Where, each time through the loop, $_ is an array reference (like to $AoA[$x] in your code); and then it is dereferenced with @$_.

Re: Help to assign a 2D-Array to another while excluding specific rows.
by mmartin (Monk) on Sep 14, 2011 at 14:55 UTC

    I know a couple of you showed how to use grep for this but could someone explain how to do this Pseudo code below, just the way it is. Please nothing too complicated, having trouble understanding most of your examples.

    So I would have a for loop and loop through line by line of all the records (@AoA which is a 2D-Array).
    Then say, using grep, "If the current line does NOT CONTAIN any element from @SPUsers (which is a single dimension array holding a couple of names).
    Then push/map the data from "the current line of @AoA" onto the end of the array @recordsToDel (also should be a 2D-Array)... Next line

    I tried this below to achieve that but not sure how to do the EXPR part of the grep command. What I get is the correct amount of elements but the output when I print each element is like "Array(0x8431c84)".


    #2D-Array of all the data, containg rows and columns @AoA = <DATA>; my @SPUsers = ("bill", "Bob", "Mike", "Steve", "mmartin"); my @recordsToDel; my $count = 0; for (my $x = 0; $x <= $#AoA; $x++) { if (grep ( @{ $AoA[$x] } !~ @SPUsers, @AoA )) { $recordsToDel[$count] = @{ $AoA[$x] }; } }


    Please let me know if I need to explain a bit more.


    Thanks,
    Matt


    .