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

I wish to extract an element from a list, ie (a,b,c) => (a,c). However this list is a tad complex

$a[0][0] = ['00', '01']; $a[0][1] = ['10', '11']; DB<69> x @a[0] 0 ARRAY(0x55bb8ce673d8) 0 ARRAY(0x55bb8ce5b430) 0 00 1 01 1 ARRAY(0x55bb8d543fb8) 0 10 1 11

The closest I've been able to get is:

DB<69> x @a[0] 0 ARRAY(0x55bb8ce673d8) 0 undef 1 ARRAY(0x55bb8d543fb8) 0 10 1 11

Using delete but I've been unable to get rid of the undef. Oh, List::UtilsBy::extract_by {...} was not successful.

Replies are listed 'Best First'.
Re: extracting a list element
by LanX (Saint) on Jan 05, 2024 at 00:45 UTC
    Using delete for arrays is almost always wrong, and it's documented:

    > delete may also be used on arrays and array slices, but its behavior is less straightforward. Although " exists" will return false for deleted entries, deleting array elements never changes indices of existing values ; use shift or splice for that.

    DB<8> $a[0][0] = ['00', '01']; DB<9> $a[0][1] = ['10', '11']; DB<10> x splice @{$a[0]},0,1 0 ARRAY(0xb40000795febe168) 0 00 1 01 DB<11> x @a 0 ARRAY(0xb40000795febbdf8) 0 ARRAY(0xb40000795fee4240) 0 10 1 11 DB<12>

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    see Wikisyntax for the Monastery

      Thanks Rolf and Annon.
      Using delete for arrays is almost always wrong...

      I disagree. Deleting an array slice is a very handy operation that I don't regard as useful almost never.

      my @a = (2 .. 9); my @i = (2, 3, 5); delete @a[@i]; my @b = grep defined, @a;

      Greetings,
      -jo

      $gryYup$d0ylprbpriprrYpkJl2xyl~rzg??P~5lp2hyl0p$
        A much saner way to undefine slices is @a[@i] = () ° ¹

        DB<26> @a = (2 .. 9); DB<27> @i = (2, 3, 5); DB<28> @a[@i] = () DB<29> @b = grep defined, @a; DB<30> x @a 0 2 1 3 2 undef 3 undef 4 6 5 undef 6 8 7 9 DB<31> x @b 0 2 1 3 2 6 3 8 4 9 DB<32> ... DB<65> p exists $a[0] 1 DB<66> p defined $a[0] DB<67>

        Like with hashes is delete not assigning undef but kicking the element's value out of existence.

        Now the size of hashes is shrinking by the number of deleted elements. But with arrays this only happens for elements at the end of the array, which is shortened then. While this is consistent with the use of exists , it might lead to unpredictable behavior.*

        This explains the stark warning.

        DB<40> @a = (0..4) DB<41> delete @a[0,2,4] DB<42> p exists $a[0] DB<43> p exists $a[1] 1 DB<44> x @a # shortened 0 undef 1 1 2 undef 3 3 DB<45> p scalar @a 4 DB<46>

        Bottom line: only use delete with arrays if you really want to differentiate between defined and exists and this shrinking behavior is anticipated.

        In order to remove array elements use splice. If you need sparse arrays, consider using hashes with numbers as keys. *

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        see Wikisyntax for the Monastery

        footnotes

        °) unfortunately is undef @arr[@slice] not DWIM.

        *) Paragraph rephrased

        ¹) and @b = grep exists, @a; is the way to go together with delete. This way you won't get caught by accidental undef values.

        update

        To better highlight that undef is an existing value but non existence is translated to undef after assignment:

        DB<56> x @a 0 undef 1 1 2 undef 3 3 DB<57> $a[5] = undef DB<58> x @a 0 undef 1 1 2 undef 3 3 4 undef. # gap 5 undef. # value DB<59> p exists $a[4] DB<60> p exists $a[5] 1 DB<61>

        From delete

        WARNING: Calling delete on array values is strongly discouraged. The notion of deleting or checking the existence of Perl array elements is not conceptually coherent, and can lead to surprising behavior.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        see Wikisyntax for the Monastery

Re: extracting a list element
by Anonymous Monk on Jan 05, 2024 at 14:38 UTC

    By extract do you mean remove? If so, you want splice. See if the following does what you want:

    #!/usr/bin/env perl
    
    use 5.010;
    
    use strict;
    use warnings;
    use YAML;
    
    my @x = ( [ [ qw{ 00 01 } ], [ qw{ 10 11 } ] ] );
    
    say 'Original @x';
    print Dump \@x;
    
    splice @{ $x[0] }, 0, 1;
    
    say 'After removing $x[0]';
    print Dump \@x;
    

    This prints

    Original @x
    ---
    -
      -
        - 00
        - 01
      -
        - 10
        - 11
    After removing $x[0]
    ---
    -
      -
        - 10
        - 11
    
Re: extracting a list element
by hippo (Archbishop) on Jan 05, 2024 at 09:36 UTC
    Oh, List::UtilsBy::extract_by {...} was not successful.

    SSCCE, please.


    🦛

      I looked into List::UtilsBy#extract_by and it's irritating iterating over the array values aliased to $_ , and only one dimension, i.e. non diving.

      There is no information of the index passed into the lambda.

      Hence one would need to define and increment it on its own, which seems too cumbersome to me.

      Or do you have a better idea?

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      see Wikisyntax for the Monastery

        Without an SSCCE (ideally in the form of a test) it's just guesswork as to what geoffleach actually wants to achieve. I'm quite content to wait until that arrives.


        🦛