Search code examples
arraysperl

How to divide an array in to two different arrays in Perl


I have an Array of Hash, and I need to divide it into two different arrays. The array of Hash looks like this:

 old_array = [
   {
      'a'=>1,
      'b'=>2,
      'c'=>3,
      'd'=>4
   },
   {  
      'e'=>5,
      'f'=>6,
      'g'=>7,
      'h'=>8
   },
  {  
      'i'=>9,
      'j'=>10,
      'k'=>11,
      'l'=>12
   },
  {  
      'm'=>13,
      'n'=>14,
      'o'=>15,
      'p'=>16
   }

];

I need to divide it into two different arrays in the manner:

   new_array_1 = [{
      'a'=>1,
      'b'=>2,
      'c'=>3,
      'd'=>4
   },
   {  
      'e'=>5,
      'f'=>6,
      'g'=>7,
      'h'=>8
   }
 ];


 new_array_2 = [{  
      'i'=>9,
      'j'=>10,
      'k'=>11,
      'l'=>12
   },
  {  
      'm'=>13,
      'n'=>14,
      'o'=>15,
      'p'=>16
   }
 ];

I have now tried the below code as mentioned in one of the answers:

     my ($new_array_1, $new_array_2) =
    map { [ $_ ] }
          @$old_array;

This is splitting the array but removing the middle hashes and, I am getting output as:

   $VAR1 = [
      {
        'c' => 3,
        'a' => 1,
        'b' => 2,
        'd' => 4
      }
    ];

$VAR1 = [
      {
        'g' => 7,
        'f' => 6,
        'e' => 5,
        'h' => 8
      }
    ];

What shall I do to get the desired output? The hashes may increase to 10 or 20 or 15, etc.


Solution

  • The question has changed significantly, where now the arrayref has not two elements but more, and it need be split so that its halves go into two arrays. Elements need not be made arrayrefs.

    Now one has to go to indices. (Well, or to alternate between new arrays while moving/copying element by element.) One way

    my $midind = ($#$old_arrayref-1)/2;  # or: (@$oldarrayref/2)-1
    
    my $new_arrayref_1 = [ @$old_arrayref[0..$midind] ];
    my $new_arrayref_2 = [ @$old_arrayref[$midind+1 .. $#$old_array] ];
    

    This preserves the original array as it was. (The syntax $#$arrayref is for the last index in the arrayref $arrayref.) If the old array doesn't matter, can do instead

    my $new_arrayref_1 = [ splice @$old_arrayref, 0, $midind+1 ];
    my $new_arrayref_2 = [ @$old_arrayref ];
    

    NOTE If elements of the original array themselves contain complex data structures (so, references) then they need be "deep-copied," for example using Storable::dclone.

    I still recommend working through resources linked in the original post (below), to learn how to work with arrays in this language.


    The original answer. The question before it was changed had the initial array(ref) with strictly two elements, and once th ey are split into two arrays they (seemingly) needed to be wrapped into arrayrefs. This should still be useful for the list of basic resources (if the reader needs that).

    There is a range of operations supported on arrays in Perl. As for basics, we can remove and return an element from front or back, with shift or pop, and can add with unshift or push.

    Then there is more, including splice you tried, and then there are libraries for more complex operations. But the basics readily solve your quest. One way

    my $old_arrayref = [ { a=>1, b=>2 }, { c=>3, d=>4 } ];
    
    my @new_array_1 = shift @$old_array;
    my @new_array_2 = shift @$old_array;
    

    (An array can be simply assigned a list of elements, including just one. But if there is anything in the array then that would be all gone after such an assignment so don't forget that.)

    Here I take the source (old_array) to be an array-reference, following the question. Now that $old_array is empty, if it had only those two elements, which are now in @new_array_N's.

    Another, even more elemental way, is to index into the array to access individual elements and copy them to new arrays. This way $old_array would stay as it is.

    Perhaps a more reasonable arrangement is to have references for these two new arrays as elements of an array, which may be what your code attempts to do. For that you only need

    my @new_array = map { [ $_ ] } @$old_array;
    

    The map applies the code in the block to each element of its input (@$old_array), returning a list, assigned to @new_array. That [] in the body constructs an anonymous array (reference), which is a scalar and as such can be placed on an array. The only element placed in this array-reference is the currently processed element of @$old_array, available in the (ubiquitous) $_ variable.

    See perlintro and perldata linked above, and links in shift. For many additional operations on lists and arrays, provided in libraries, see List::Util and List::MoreUtils for starters.

    For references see perlreftut and for arrays with references see perllol (lists-of-lists) and perldsc (data structures cookbook).

    A complete program

    use warnings;
    use strict;
    use feature 'say';
    use Data::Dumper;
    
    my @old_array = ( { 'a'=>1, 'b'=>2 }, { 'c'=>3, 'd'=>4 } );  
    
    my @new_array = map { [ $_ ] } @old_array;
    
    say Dumper \@new_array;
    

    This prints (just condensed, for readability in this post)

    $VAR1 = [
              [ { 'a' => 1, 'b' => 2 } ],
              [ { 'c' => 3, 'd' => 4 } ]
            ];
    

    If the new array(s) need be array-references as well

    my $new_arrayref_1 = [ shift @$old_array ];
    my $new_arrayref_2 = [ shift @$old_array ];
    

    In the second example, and in the short "complete program"

    my $new_arrayref = [ map { [ $_ ] } @$old_array ];