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

I need to compare each element in one 2D array (array1_split_2D) with all of the element in another 2D array (array2_split_2D). But the problem here is the size for the array is different. So i want to use splice to delete the first element inside the array2_split_2D so that it will keep on comparing with the element in array1_split_2D. I put inside while loop so that if the (size of column for array1_split_2D != size of column of array2_split_2D), it will keep on looping and splice the element. But my code here delete all the array inside array2_split_2D while looping so it will be infinite looping in the while loop.

@array1 = ( 'ux_prim_clk', 'ux_side_clk', 'ux_xtal_frm_refclk'); @array2 = ( 'ccu_ux_xtal_frm_refclk_ack', 'ibbs_ux_prim_clkack', 'sbr_ux_side_clkack'); foreach(@array1){ @array1_split_2D = map {[split /_/,$_]} @array1; } foreach(@array2){ @array2_split_2D = map{[split /_/,$_]} @array2; } $match = 0; $highest_match_rate = 0; $array1_row_size = @array1_split_2D; $array2_row_size = @array2_split_2D; for ($i=0; $i<$array1_row_size; $i++) { print "\ninside first for loop\n"; $array1_column_size = @{$array1_split_2D[$i]}; #print "\nfirst column size = $array1_column_size\n"; #print "first: $first_line[$i][0]\nsecond: $second_line[$i_sec][$j +]"; for ($i_sec=0; $i_sec<$array2_row_size; $i_sec++) { $array2_column_size = @{$array2_split_2D[$i_sec]}; while ($array1_column_size != $array2_column_size){ print "\ninside while loop\n"; print "\ncolumn size array1: $array1_column_size\n"; print "column size array2: $array2_column_size\n"; for ($j=0; $j<$array1_column_size; $j++){ print "first[$i][$j]: $array1_split_2D[$i][$j] second[ +$i_sec][$j]: $array2_split_2D[ if ($array1_split_2D[$i][$j] eq $array2_split_2D[$i_se +c][$j]){ $match = $match + 1; print "$match\n"; } } $remove_first_column = splice @{$array2_split_2D[$i_sec]}, + 0, 1; print "remove = $removed\n"; } } }
  • Comment on remove element from 2D array after comparing it with other 2D array
  • Download Code

Replies are listed 'Best First'.
Re: remove element from 2D array after comparing it with other 2D array (updated)
by AnomalousMonk (Archbishop) on Apr 28, 2019 at 03:52 UTC

    I'm not sure just what you are looking for, but try something like:

    c:\@Work\Perl\monks>perl -e "use warnings; use strict; ;; use List::MoreUtils qw(uniq); use Data::Dumper; ;; my @array1 = ('ux_prim_clk', 'ux_side_clk', 'ux_xtal_frm_refclk', 'pr +im_ibbs'); my @array2 = ('ccu_ux_xtal_frm_refclk_ack', 'ibbs_ux_prim_clkack', 's +br_ux_side_clkack'); ;; my @array1_split_2D = map [ split /_/ ], @array1; my @array2_split_2D = map [ split /_/ ], @array2; ;; print Dumper \@array1_split_2D; print Dumper \@array2_split_2D; ;; for my $ar_array_1_cols (@array1_split_2D) { for my $ar_array_2_cols (@array2_split_2D) { my %array_2_colums = map { $_ => 1 } @$ar_array_2_cols; my @intersection = grep exists $array_2_colums{$_}, uniq @$ar_arr +ay_1_cols; ;; print qq{(@$ar_array_1_cols) elements in (@$ar_array_2_cols): \n} +; if (@intersection) { print qq{ '$_'} for @intersection; } else { print ' - no intersection -'; } print qq{\n}; } print qq{\n}; } " $VAR1 = [ [ 'ux', 'prim', 'clk' ], [ 'ux', 'side', 'clk' ], [ 'ux', 'xtal', 'frm', 'refclk' ], [ 'prim', 'ibbs' ] ]; $VAR1 = [ [ 'ccu', 'ux', 'xtal', 'frm', 'refclk', 'ack' ], [ 'ibbs', 'ux', 'prim', 'clkack' ], [ 'sbr', 'ux', 'side', 'clkack' ] ]; (ux prim clk) elements in (ccu ux xtal frm refclk ack): 'ux' (ux prim clk) elements in (ibbs ux prim clkack): 'ux' 'prim' (ux prim clk) elements in (sbr ux side clkack): 'ux' (ux side clk) elements in (ccu ux xtal frm refclk ack): 'ux' (ux side clk) elements in (ibbs ux prim clkack): 'ux' (ux side clk) elements in (sbr ux side clkack): 'ux' 'side' (ux xtal frm refclk) elements in (ccu ux xtal frm refclk ack): 'ux' 'xtal' 'frm' 'refclk' (ux xtal frm refclk) elements in (ibbs ux prim clkack): 'ux' (ux xtal frm refclk) elements in (sbr ux side clkack): 'ux' (prim ibbs) elements in (ccu ux xtal frm refclk ack): - no intersection - (prim ibbs) elements in (ibbs ux prim clkack): 'prim' 'ibbs' (prim ibbs) elements in (sbr ux side clkack): - no intersection -
    See List::MoreUtils or List::Util for  uniq() (which may not be strictly necessary) and Data::Dumper (which is a core module) for  Dumper.

    Update 1: See also How do I compute the difference of two arrays? How do I compute the intersection of two arrays? in perlfaq4 (you seem to be looking for an intersection).

    Update 2: Changed example code to show extraction of intersection elements to  @intersection array separately from display of the intersection.


    Give a man a fish:  <%-{-{-{-<

      Thank you so much for the reply. This is exactly what i need. Thank you!!

Re: remove element from 2D array after comparing it with other 2D array
by haukex (Archbishop) on Apr 28, 2019 at 11:30 UTC

    Welcome to the Monastery, Newbie95.

    Please be aware that the code you've posted does not compile, does not Use strict and warnings, and you haven't shown what your expected output is - please have a look at How do I post a question effectively? and Short, Self-Contained, Correct Example.

    Looking at your code, the first thing that jumps out at me is the first two foreach loops: on every iteration of the loop, you're overwriting the contents of @array1_split_2D and @array2_split_2D. But since on every iteration of the loop you're processing the entire @array1 and @array2 with map, really the foreach loops aren't necessary at all.

    Next, I see that there are quite a few variables that are only being used once, so perhaps you haven't provided us with enough context, but to simplify the code, I'm going to remove them, and also remove the prints, and any other code that now becomes useless, such as the inner if and for ($j=0;.... I also renamed $i_sec to $j, since the latter got freed up.

    Note that you don't need to use scalar variables to store the sizes of arrays, as long as you use the array in scalar context, it'll return the number of elements. So for example, instead of $array1_row_size = @array1_split_2D; ... $i<$array1_row_size you can just say $i<@array1_split_2D - it's the same, as long as the array doesn't change its size! Also, loops such as for ($i=0; $i<@array1_split_2D; $i++) can be written a bit nicer in Perl using Range Operators, so for example: for my $i (0..$#array1_split_2D), where $#array1_split_2D returns the index of the last element (perldata).

    So here's your code, rewritten like I described:

    use warnings; use strict; my @array1 = ('ux_prim_clk', 'ux_side_clk', 'ux_xtal_frm_refclk'); my @array2 = ('ccu_ux_xtal_frm_refclk_ack', 'ibbs_ux_prim_clkack', 'sbr_ux_side_clkack'); my @array1_split_2D = map { [ split /_/, $_ ] } @array1; my @array2_split_2D = map { [ split /_/, $_ ] } @array2; for my $i ( 0 .. $#array1_split_2D ) { for my $j ( 0 .. $#array2_split_2D ) { my $array2_column_size = @{$array2_split_2D[$j]}; while ( @{$array1_split_2D[$i]} != $array2_column_size ) { splice @{$array2_split_2D[$j]}, 0, 1; } } }

    So hopefully now it's easier to read and understand. You may notice that even though I removed $array1_column_size = @{$array1_split_2D[$i]};, I did not do the same for $array2_column_size = @{$array2_split_2D[$i_sec]}; - why? Because the size of @{$array2_split_2D[$j]} changes due to the splice, but in your original code you've saved the original size in $array2_column_size. Now if you look at your while loop, I hope it becomes clear where the infinite loop is coming from?

    The fix is to always access the current size of @{$array2_split_2D[$j]} in the condition of the while. Also, the condition is !=, but what if @{$array2_split_2D[$j]} happens to be shorter than @{$array1_split_2D[$i]} to begin with? Then you've got another infinite loop. The solution there is to change the condition to <.

    Okay, so now your code is hopefully easier to understand, plus it runs. Next, I'd have a look at the Basic debugging checklist, in particular, I like to use Data::Dump to output data structures (Data::Dumper is an alternative that's in the core):

    use warnings; use strict; use Data::Dump; my @array1 = ('ux_prim_clk', 'ux_side_clk', 'ux_xtal_frm_refclk'); my @array2 = ('ccu_ux_xtal_frm_refclk_ack', 'ibbs_ux_prim_clkack', 'sbr_ux_side_clkack'); my @array1_split_2D = map {[split /_/,$_]} @array1; my @array2_split_2D = map {[split /_/,$_]} @array2; dd "before", \@array1_split_2D, \@array2_split_2D; for my $i (0..$#array1_split_2D) { for my $j (0..$#array2_split_2D) { dd $i, $j; dd $array1_split_2D[$i], $array2_split_2D[$j]; while ( @{$array1_split_2D[$i]} < @{$array2_split_2D[$j]} ) { splice @{$array2_split_2D[$j]}, 0, 1; } dd $array2_split_2D[$j]; } } dd "after", \@array1_split_2D, \@array2_split_2D;

    I don't know if this output is anything like what you want, since you didn't say what you want. Now, based purely on the input data, my guess is that maybe you're doing some kind of a longest common substring search? Note that there are several modules on CPAN to help with that, such as String::LCSS.

    #!/usr/bin/env perl use warnings; use strict; use String::LCSS qw/lcss/; my @array1 = ('ux_prim_clk', 'ux_side_clk', 'ux_xtal_frm_refclk'); my @array2 = ('ccu_ux_xtal_frm_refclk_ack', 'ibbs_ux_prim_clkack', 'sbr_ux_side_clkack'); for my $one (@array1) { for my $two (@array2) { my $lcss = lcss($one, $two); printf "%-18s / %-26s -> %s\n", $one, $two, $lcss; } } __END__ ux_prim_clk / ccu_ux_xtal_frm_refclk_ack -> ux_ ux_prim_clk / ibbs_ux_prim_clkack -> ux_prim_clk ux_prim_clk / sbr_ux_side_clkack -> _clk ux_side_clk / ccu_ux_xtal_frm_refclk_ack -> ux_ ux_side_clk / ibbs_ux_prim_clkack -> _clk ux_side_clk / sbr_ux_side_clkack -> ux_side_clk ux_xtal_frm_refclk / ccu_ux_xtal_frm_refclk_ack -> ux_xtal_frm_refclk ux_xtal_frm_refclk / ibbs_ux_prim_clkack -> ux_ ux_xtal_frm_refclk / sbr_ux_side_clkack -> ux_

    On the other hand, if you're simply trying to determine if one of the strings contains the other string exactly, you might want to have a look at my node Building Regex Alternations Dynamically. It would be possible to adapt a regex solution such that it only matches elements separated by underscores, as your original code seems to be doing.

      Hi haukex. Thank you so much for the guidance. I understand now why my code did not work the way i want. Thank you!

      Hi again Haukex. What i need to do is to make sure that my @array1 sequence is the same with @array2.

      For example if my input is this:

      my @array1 = ('ux_prim_clk', 'ux_side_clk', 'ux_xtal_frm_refclk');

      my @array2 = ('ccu_ux_xtal_frm_refclk_ack', 'ibbs_ux_prim_clkack','sbr_ux_side_clkack');

      And, after doing the comparison, my output is:

      @array1 = ('ux_xtal_frm_refclk', 'ux_prim_clk', 'ux_side_clk')

      @array2 = ('ccu_ux_xtal_frm_refclk_ack', 'ibbs_ux_prim_clkack','sbr_ux_side_clkack')

      I want to use @array2 as reference and make sure that @array1 is following the same sequence as @array2. Note that i cannot simply use name to compare because the name of element inside @array1 & @array2 can be changed.

      Let say from @array1, the first row's element is ('ux_prim_clk') and for the @array2, the first row's element is ('ccu_ux_xtal_frm_refclk_ack'). Now, i want to compare these two but i want to compare it word by word (Eg: ux & ccu, prim & ux, clk & xtal).

      But the constraint now is that my @array2 first row's element has different size with @array1. So that's why i want to splice array2 first row's element and compare it with the first row's element of @array1. My @array1 is maintain throughout the comparison.

      For example (using first row's element for both arrays):

      @array1 = (ux, prim, clk) @array2 = (ccu, ux, xtal, frm, refclk, ack)

      1. compare: ux & ccu, prim & ux, clk & xtal.

      2. compare: ux & ux, prim & xtal, clk & frm.

      3. compare: ux & xtal, prim & frm, clk & refclk.

      4. compare: ux & frm, prim & refclk, clk & ack.

      Each time it compares (1 - 4), i want to store the number of match inside let say $match variable, and overwrite it if it has highest number of match. For example:

      $highest_match = 0; if ($match > $highest_match){ $highest_match = $match; } $highest_percentage = ($highest_match/no. of element in array1's row)* +100;

      I will then plan to do condition if (highest_percentage > 50%), i will store the entire row of @array2 inside another array.

      So i will end up with the output that i want inside this new array.

      I hope this clears your confusion. I'm sorry i really bad at explaining. I only learn c language before so this language is very new to me. I really appreciate your effort.

        Ok, I think I understand your algorithm, it seems very similar to the LCSS idea I mentioned and showed, except you want to split words on underscores, and I think you want to compare each word exactly? (In that case, when comparing sbr_ux_side_clkack with ux_side_clk, the match would only be ux_side.)

        I could implement your algorithm more or less literally, although in the following I haven't implemented the 50% rule you mentioned - it just picks the @array1 element with the best match, and also there is currently no protection against picking the same @array1 element twice. Instead of spliceing the @array2 elements, which is a destructive operation, I use different offsets. However, as you can tell, the whole thing gets kind of complex, plus, because of the four (!) nested loops, if the strings and/or arrays get longer, it will be less and less performant! But maybe this is a good starting point anyway.

        By the way, @array1 and @array2 aren't very descriptive names, I recommend you choose some better (more descriptive) variable names.

        use warnings; use strict; use Data::Dump qw/dd pp/; my @array1 = ('ux_prim_clk', 'ux_side_clk', 'ux_xtal_frm_refclk'); my @array2 = ('ccu_ux_xtal_frm_refclk_ack', 'ibbs_ux_prim_clkack', 'sbr_ux_side_clkack'); my @aoa1 = map { [ split /_/, $_ ] } @array1; my @output; # Using @array2 for the basis of ordering, so loop over that first for my $a2 ( map { [ split /_/, $_ ] } @array2 ) { #print "##### "; dd $a2; # debug # Now look through @array1 for the best match my ($highest_match, $highest_match_at_a1idx) = (-1); for my $i ( 0 .. $#aoa1 ) { # in this loop, keep track of index my $a1 = $aoa1[$i]; # The following code relies on @$a1 >= @$a2, so check that if ( @$a2 < @$a1 ) { warn "Skipping ".pp($a2, $a1); next } # Try matching $a1 against $a2 at different offsets for my $offset ( 0 .. @$a2 - @$a1 ) { my $match = 0; # Count the number of matching elements at each offset for my $j ( 0 .. $#$a1 ) { if ( $a2->[$offset+$j] eq $a1->[$j] ) { $match++ } else { last } # failed to match, stop looking } # If this index and offset matches better, record that if ( $match && $match > $highest_match ) { #dd $a2, $offset, $a1, $match; # debug $highest_match = $match; $highest_match_at_a1idx = $i; } } } if ( defined $highest_match_at_a1idx ) { push @output, $array1[$highest_match_at_a1idx] } else { warn "Failed to find match for ".pp($a2) } } dd @array1; dd @array2; dd @output; __END__ ("ux_prim_clk", "ux_side_clk", "ux_xtal_frm_refclk") ( "ccu_ux_xtal_frm_refclk_ack", "ibbs_ux_prim_clkack", "sbr_ux_side_clkack", ) ("ux_xtal_frm_refclk", "ux_prim_clk", "ux_side_clk")
Re: remove element from 2D array after comparing it with other 2D array
by AnomalousMonk (Archbishop) on Apr 28, 2019 at 22:07 UTC