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

Feel like I'm missing something here. If I wanted to load an array with elements 1,,4,5,,, and act on each element (including the null/undef elements), how do I do that?
$_ = '1,,3,4,,,'; # a string of 7 possible values @a = split(','); print scalar(@a) . " items in the array\n"; # Sure wish this was 7 print "The second item is $a[1]\n"; # sure wish this was null foreach (@a) { ++$count; print "Item $count is $_\n"; }
Right now since I know the number of items I'm expecting, I add a
$a[7] = undef; pop(@a);
after the split, but that feels dirty.

Replies are listed 'Best First'.
Re: But I want null values in my array
by Fletch (Bishop) on Mar 11, 2020 at 19:05 UTC

    You need to give split an explicit special limit of -1 something negative if you want trailing empty fields to be returned. The relevant bit of the doc is below:

    If LIMIT is omitted (or, equivalently, zero), then it i +s usually treated as if it were instead negative but with + the exception that trailing empty fields are stripped (empt +y leading fields are always preserved); if all fields are + empty, then all fields are considered to be trailing (and are +thus stripped in this case). Thus, the following: print join(':', split(',', 'a,b,c,,,')), "\n"; produces the output 'a:b:c', but the following: print join(':', split(',', 'a,b,c,,,', -1)), "\n"; produces the output 'a:b:c:::'.

    Edit: tweaked wording.

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

Re: But I want null values in my array
by kcott (Archbishop) on Mar 11, 2020 at 19:51 UTC

    G'day sidmuchrock,

    I looks like you might be working with CSV data. If that's the case, Text::CSV already does what you want (if you also have Text::CSV_XS installed, it will run faster). Text::CSV also solves many other issues you might encounter with CSV data; e.g. fields containing the separator character, such as 800,900,"1,000". Unless you're doing this as a purely academic exercise, this really isn't a wheel you should be reinventing.

    Here's a quick example. I used your posted data and added a bit more to show some features, including: just one, zero-length element; and the embedded separator character I showed above.

    #!/usr/bin/env perl use strict; use warnings; use Text::CSV; my $csv = Text::CSV::->new(); while (my $row = $csv->getline(*DATA)) { # Do something with all elements, e.g. print 'Elements: ', 0+@$row, '; Last: |', $row->[-1], "|\n"; } __DATA__ 1,,3,4,,, 1,,3,4,,,not_empty , 800,900,"1,000"

    Output:

    Elements: 7; Last: || Elements: 7; Last: |not_empty| Elements: 1; Last: || Elements: 2; Last: || Elements: 3; Last: |1,000|

    — Ken

      Don't forget blank_is_undef
Re: But I want null values in my array
by Marshall (Canon) on Mar 11, 2020 at 23:59 UTC
    There is one additional "gotcha" when and if you split on whitespace... There is a difference in how leading whitespace is handled depending upon how you specify what to split upon. There is a difference between ' ' and / / or /\s+/. This is demo'ed below. Also shown is the -1 limit previously discussed although that limit does not affect this leading whitespace behaviour.
    use strict; use warnings; use Data::Dump qw(pp); my $str = " 1 2 3 "; # Note: leading whitespace in string... # special behaviour when splitting on whitespace: # leading null field: my @with_regex = split (/ /,$str,-1); pp \@with_regex; # ["", 1, 2, 3, ""] @with_regex = split (/\s+/,$str,-1); pp \@with_regex; # ["", 1, 2, 3, ""] # no leading null field: my @with_char = split (' ',$str,-1); pp \@with_char; # [1, 2, 3, ""]
    Update: I guess also worthy of mention, "", null string does not mean undef.
Re: But I want null values in my array
by AnomalousMonk (Archbishop) on Mar 11, 2020 at 20:45 UTC

    I agree that Text::CSV is almost certainly the best way, but here's a reinvented wheel (note the leading "null" field introduced into the example):

    c:\@Work\Perl\monks>perl -wMstrict -MData::Dump -le "my $s = ',1,,3,4,,,'; ;; my @ra = map { m{ \A \s* \z }xms ? undef : $_ } split ',', $s, -1; dd \@ra; " [undef, 1, undef, 3, 4, undef, undef, undef]


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

Re: But I want null values in my array
by hippo (Archbishop) on Mar 12, 2020 at 10:49 UTC

    I agree with my learned brethren that a module such as Text::CSV_XS would be the best practice way to go here. However, for an example of how to do this otherwise and assuming that you really do want undef when you say "null", here is an SSCCE.

    use strict; use warnings; use Test::More tests => 2; my $have = '1,,3,4,,,'; my @want = (1, undef, 3, 4, undef, undef, undef); my @try = map { length ($_) ? $_ : undef } split (',', $have, -1); is_deeply \@try, \@want, 'Array is correct'; # Your criterion my $count = 0; foreach (@try) { ++$count; } is $count, 7, 'Count is correct';