http://www.perlmonks.org?node_id=482308

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

Hello ... I am trying to figure out, which operator to use; pop, shift or splice. My goal is, as an option is selected from user input, pop or splice or shift elements off ( those selected from user input ) and repopulate array with reamaining elements.
Scenerois: as numbers are selected remove elements from array A repopulate list with what is remaining into array A print array A as numbers are selected remove elements from array A repopulate remaining numbers in to array B print array B - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +- - - - @ArrayA=(0 .. 1000); print @array; ## User now sees selection list. This could be replace +d with splice or shift or pop foreach (splice @ArrayA, @numbers to remove, scalar of @numbers to rem +ove, @ArrayA) { print $_; } # Array A now has 05-1000 OR @ArrayA=(0 .. 1000); print @array; ## User now sees selection list. This could be replace + with a loop that is popping, shifting or splicing foreach (@ArrayA) { shift @number to remove; } print @ArrayA # Array A now has 05-1000 OR @ArrayA=(0 .. 1000); print @array; ## User now sees selection list. This could be replace +d with splice or shift or pop foreach (splice @ArrayA, @numbers to remove, scalar of @numbers to rem +ove, @ArrayB) { print $_; } #ArrayB now has 05 - 1000

Replies are listed 'Best First'.
Re: popping, shifting or splicing an array???
by revdiablo (Prior) on Aug 09, 2005 at 17:30 UTC

    Your question is a bit vague, but if you're looking for the general purpose tool, it's splice. The others, shift and pop, can only perform a subset of what splice can do. Admittedly, this is a very useful subset that would be a hassle to use splice for all the time, but when you want general purpose you use general purpose.

    In your case, it would be nice if you could collapse the user's selection into a set of ranges. If you don't want to do that, you could easily enough splice off one element at a time. The trick here is to iterate on the to-be-spliced indexes in descending order, otherwise you'll have to recalculate their new positions each time. Example:

    my @array = (1 .. 1000); my @toremove = (7,1,6,5,3); for (sort { $b <=> $a } @toremove) { splice @array, $_, 1; }
    # another way to to it: for (reverse sort @toremove) { ... }

    Update: the Other Way was flawed. It didn't do a numeric sort.

Re: popping, shifting or splicing an array???
by davidrw (Prior) on Aug 09, 2005 at 17:24 UTC
    Depends where in the array you want to cut stuff out and if you're rmoving a single element at a time or multiple elements... splice is the most general (i think any shift or pop can be re-written as a splice), whereas shift and pop only remove a single element, and only from the very front or back (respectively) of the array.
      perldoc -f splice
      perldoc -f shift
      perldoc -f pop

    so, to summarize:
    if only one element from front, then shift
    else if only one element from back, then pop
    else splice

    (Note also there are probablly different ways besides splice as well -- e.g. could probably rewrite using map/grep/array slices)
      I am looking to spice off more than one element possibly up to 10 and no larger than 20 of those selected numbers from user input. 1..1000 user selects 1..5 splice array 1..5 return array with 6-1000 print new array 6-1000
Re: popping, shifting or splicing an array???
by injunjoel (Priest) on Aug 09, 2005 at 18:50 UTC
    Greetings all,
    Sounds like a job for grep to me.
    Going with the
    as numbers are selected remove elements from array A repopulate list with what is remaining into array A print array A
    #!/usr/bin/perl -w use strict; use Dumpvalue; my $d = new Dumpvalue; #testing my @ArrayA = (0 .. 100); print "before\n"; $d->dumpValues(\@ArrayA); print "\n"; #more testing. my @numbers_to_remove = (14 .. 36, 50 .. 63, 89, 91, 94); #here we reassign @ArrayA with the return value from #our do{} block. @ArrayA = do { #make a local copy of @ArrayA as is to begin with my @tmp = @ArrayA; #map returns a list which comes in handy here map{ #make a local copy of the incoming elements from #our local copy in @tmp my $key = $_; #use a ternary operator to make sure we are #not including numbers from our #@numbers_to_remove list. If it is not #found return it otherwise return an #empty list. (!grep /^$key$/, @numbers_to_remove) ? $key : (); }@tmp; #for each element in @tmp }; print "After\n"; $d->dumpValues(\@ArrayA); print "\n";

    Seems to work fine for me but I have not benchmarked this code.
    Is this what you are looking for?

    After re-reading the post I would suggest going with an Array of hashes depending on what is contained in the elements of @ArrayA. Hypothetically lets say you have a hash on information returned from a database query and you store a reference to each hash in the array @ArrayB. Then let the user select which elements to remove from the array based on some id.
    use Dumpvalue; my $d = new Dumpvalue; #create the hypothetical structure. An array of #hash references each with an id field in it. my @ArrayB = map{{id=>$_}}(0 .. 100); $d->dumpValues(\@ArrayB); print "\n"; #more testing. #these would correspond to id's to remove in our hypothetical #scenario. my @ids_to_remove = (14 .. 36, 50 .. 63, 89, 91, 94); @ArrayB = do{ my @tmp = @ArrayB; map{ my $key = $_->{id}; (!grep /^$key$/, @ids_to_remove) ? $_ : () ; }@tmp; }; $d->dumpValues(\@ArrayB); exit;

    Update!
    I added some comments to the first block of code to hopefully explain what is going on.


    -InjunJoel
    "I do not feel obliged to believe that the same God who endowed us with sense, reason and intellect has intended us to forego their use." -Galileo
      InjunJoel, looks good, but will you explain this in english. I read from bottom up correct?
      Joel, I have to disagree. An array of hashes is really not necessary b/c I do not need to lookup or reference any element by name. What is in the elements is H02043-H02999 for example. All I need is once the user selection is stored in my tape_import array I need to splice these selections out of the main array and present an updated main array or a whole new array. Here is my code thus far:
      sub import99 { print $q->h3 ({-style=>'Color:#0000CC'},'Please Choose 9940 Tape Ran +ge for Import.'), $q->h3 ({-style=>'Color:#CC3300'},'Note: Inject Tapes via Acsls Prior +To Import!'); my @H99tapes =('H02043' .. 'H02999'); ##--##MAINARRAY my @UserH99tapes = @H99tapes;##--## USER SELECTIONS print $q->scrolling_list('htapes99',[@H99tapes],[$H99tapes[0]],10,-mul +tiple=>'true',my %attributes), $q->submit('form','Submit'), $q->reset; ##--BEGINIMPORT ##--## AS H NUMBERS ARE SELECTED, REMOVE SUBMITTED ELEMENTS ##--## FRO +M USER ARRAY ##--## REPOPULATE LIST INTO NEW ARRAY,PRINT NEW ARRAY my @tape_imports = $q->param(htapes99); ##--## SELECTED BY USER my $tape_count = scalar @tape_imports; $[=0; foreach (@tape_imports) { splice (@H99tapes,0,$tape_count,@H99tapes) } } ##--## END ROUTINE import99 ##--##
        Greetings again,
        Okay so maybe the hash in not in order but I have a few questions about your current approach.
        Since you are allowing for multiple selections in your scrolling_list
        print $q->scrolling_list('htapes99',[@H99tapes],[$H99tapes[0]],10,-mul +tiple=>'true',my %attributes);
        if the user selected four elements from random places in the list then the first five values are clipped from the list, four times. Not the elements the user selected.
        my $tape_count = scalar @tape_imports; foreach (@tape_imports) { splice (@H99tapes,0,$tape_count,@H99tapes) }
        It seems like each time through your foreach (hypothetically four times) you clip the the elements in @H99tapes from 0 to $tape_count (in this case 4) so five elements are removed from the beginning of the array. Also by replacing the missing values with the entire list again you are increasing the size of your list almost exponentially, at least by a factor of scalar(@H99tapes) - (scalar(@tape_imports) + 1). In a test I ran with your code and four random values the resulting size of @H99tapes was 15252! Its starting size was 957.
        The impetus behind my hash suggestion was that you could use the map to remove those elements with the same value as those the user selected rather than trying to manipulate the list based on positional index, however looking at your code a hash is not in order, yet the logic still is. So it seems like you would want to remove those values stored in @tape_imports from the @H99tapes list. In that case you could use the first example from my previous post.
        my @H99tapes =('H02043' .. 'H02999'); #...form stuff here my @tape_imports = $q->param(htapes99); #clean out all the values in @tape_imports from @H99tapes @H99tapes = do{ my @a = @H99tapes; #make a local copy of our list #now @a gets fed element by element into our map block #and its value is assigned to $_ map { #local copy of incoming value for map. We do this #because grep also assigns its values to $_ so #in order to not confuse things we assign it to a #locally scoped var $v my $v = $_; #check if this value is in the forbidden #list @tape_imports or not. We anchor the #regexp to avoid partial matches from other #values like '90' matching against '1905'. (!grep /^$v$/, @tape_imports) ? $v : (); } @a; };
        Does that makes sense? Or am I not getting it? I think splice might not be what you are looking for since it deals with postional indexing rather than element values. You would need to know what postions in your @H99tapes array the user selected elements are at in order to accurately splice them out. Since map returns a list we can just re-write the @H99tapes array with a grep call to filter out unwanted values, regardless of position.

        -InjunJoel
        "I do not feel obliged to believe that the same God who endowed us with sense, reason and intellect has intended us to forego their use." -Galileo
Re: popping, shifting or splicing an array???
by shiza (Hermit) on Aug 09, 2005 at 17:33 UTC
    Since you are dealing with numbers you could just set the value of an array element to its' index. Instead of getting rid of a selected value, you could set it equal to undef when selected.

    A benefit to doing this way would be the minute amount of code needed to maintain your array.

    This is probably not the best way to handle this programatically, but it would work.

    I'm sure there are a lot of arguments against doing it this way and am interested in hearing them.
Re: popping, shifting or splicing an array???
by jch341277 (Sexton) on Aug 09, 2005 at 18:36 UTC

    I know that this doesn't technically answer your question - but wouldn't this be easier using a hash?