In Search of Elegant Code - Change only the first null element in an array

Discussion in 'Perl Misc' started by usenet@DavidFilmer.com, Oct 5, 2005.

  1. Guest

    Suppose I have an array like this:

    my @stuff = ("Just", "Some", "", "", "Text");

    I want to replace the first (and ONLY the first) null element with some
    string. If the array has no null values, don't do anything to it
    (unless the array is empty, in which case add the string as an
    element).

    This almost works:

    do {$stuff[$_] =~ s/^$/Null/ && last} for ( 0 .. @stuff-1 ) ;

    but it won't affect an empty array as desired (so I need an extra line
    to test and push).

    I can do it REALLY ugly like this:

    my $index = 0;
    until ($stuff[$index] =~ s/^$/Null/ || $index >= @stuff - 1) {
    $index++;
    }

    It works, but... Yuck. Any ideas for a more elegant approach?
    , Oct 5, 2005
    #1
    1. Advertising

  2. wrote:
    > Suppose I have an array like this:
    >
    > my @stuff = ("Just", "Some", "", "", "Text");
    >
    > I want to replace the first (and ONLY the first) null element with some
    > string. If the array has no null values, don't do anything to it
    > (unless the array is empty, in which case add the string as an
    > element).
    >
    > This almost works:
    >
    > do {$stuff[$_] =~ s/^$/Null/ && last} for ( 0 .. @stuff-1 ) ;
    >
    > but it won't affect an empty array as desired (so I need an extra line
    > to test and push).
    >
    > I can do it REALLY ugly like this:
    >
    > my $index = 0;
    > until ($stuff[$index] =~ s/^$/Null/ || $index >= @stuff - 1) {
    > $index++;
    > }
    >
    > It works, but... Yuck. Any ideas for a more elegant approach?


    hmm, how about:

    unless (@stuff) {
    push( @stuff, $string );
    }

    for (@stuff) {
    if ($_ eq '') {
    $_ = $string;
    last;
    }
    }
    it_says_BALLS_on_your forehead, Oct 5, 2005
    #2
    1. Advertising


  3. >
    > hmm, how about:
    >
    > unless (@stuff) {
    > push( @stuff, $string );
    > }
    >
    > for (@stuff) {
    > if ($_ eq '') {
    > $_ = $string;
    > last;
    > }
    > }


    actually, that should be:

    if (@stuff) {
    for (@stuff) {
    if ($_ eq '') {
    $_ = $string;
    last;
    }
    }
    }
    else {
    push( @stuff, $string );
    }


    ....or by "elegant" did you mean a one-liner?
    it_says_BALLS_on_your forehead, Oct 5, 2005
    #3
  4. Guest

    an almost 1 liner:
    {
    if (!@stuff) { $stuff[0]='Null'; next; }
    do { ($stuff[$_] =~ s/^$/Null/ && last) } for (0 .. @stuff-1);
    }
    , Oct 6, 2005
    #4
  5. John Bokma Guest

    wrote:

    > It works, but... Yuck. Any ideas for a more elegant approach?


    How about: program it readable? Elegant to me :)

    --
    John Small Perl scripts: http://johnbokma.com/perl/
    Perl programmer available: http://castleamber.com/
    I ploink googlegroups.com :)
    John Bokma, Oct 6, 2005
    #5
  6. Guest

    wrote:
    > Suppose I have an array like this:
    >
    > my @stuff = ("Just", "Some", "", "", "Text");
    >
    > I want to replace the first (and ONLY the first) null element with some
    > string. If the array has no null values, don't do anything to it
    > (unless the array is empty, in which case add the string as an
    > element).
    >
    > This almost works:
    >
    > do {$stuff[$_] =~ s/^$/Null/ && last} for ( 0 .. @stuff-1 ) ;
    >
    > but it won't affect an empty array as desired (so I need an extra line
    > to test and push).
    >
    > I can do it REALLY ugly like this:
    >
    > my $index = 0;
    > until ($stuff[$index] =~ s/^$/Null/ || $index >= @stuff - 1) {
    > $index++;
    > }
    >
    > It works, but... Yuck. Any ideas for a more elegant approach?





    Something like:


    use List::MoreUtils qw(firstidx);

    my @ar = ('foo', '', 'baz', '', '?');
    $ar[ firstidx { defined $_ and $_ eq '' } @ar ] = 'bar';

    --
    Hope this helps,
    Steven
    , Oct 6, 2005
    #6
  7. Guest

    wrote:

    ....
    >
    > Something like:
    >
    >
    > use List::MoreUtils qw(firstidx);
    >
    > my @ar = ('foo', '', 'baz', '', '?');
    > $ar[ firstidx { defined $_ and $_ eq '' } @ar ] = 'bar';
    >



    Unfortunately, this will only work if you know
    ahead of time that the array contains an empty
    strings -- else I end up modifying the last
    element of the array (because firstidx then
    returns -1).

    --
    Regards,
    Steven
    , Oct 6, 2005
    #7
  8. Bob Walton Guest

    Re: In Search of Elegant Code - Change only the first null elementin an array

    wrote:

    > Suppose I have an array like this:
    >
    > my @stuff = ("Just", "Some", "", "", "Text");
    >
    > I want to replace the first (and ONLY the first) null element with some
    > string. If the array has no null values, don't do anything to it
    > (unless the array is empty, in which case add the string as an
    > element).
    >
    > This almost works:
    >
    > do {$stuff[$_] =~ s/^$/Null/ && last} for ( 0 .. @stuff-1 ) ;
    >
    > but it won't affect an empty array as desired (so I need an extra line
    > to test and push).
    >
    > I can do it REALLY ugly like this:
    >
    > my $index = 0;
    > until ($stuff[$index] =~ s/^$/Null/ || $index >= @stuff - 1) {
    > $index++;
    > }
    >
    > It works, but... Yuck. Any ideas for a more elegant approach?
    >


    How about:

    use warnings;
    use strict;
    use Data::Dumper;
    my $somestring='somestring';
    my @stuff = ("Just", "Some", "", "", "Text");
    my %h;@h{reverse @stuff}=reverse 0..$#stuff;
    $h{$somestring}=$h{''};delete $h{''};
    @stuff[values %h]=keys %h;
    print Dumper \@stuff;

    --
    Bob Walton
    Email: http://bwalton.com/cgi-bin/emailbob.pl
    Bob Walton, Oct 6, 2005
    #8
  9. Bob Walton Guest

    Re: In Search of Elegant Code - Change only the first null elementin an array

    Bob Walton wrote:

    > wrote:

    ....

    > How about:
    >
    > use warnings;
    > use strict;
    > use Data::Dumper;
    > my $somestring='somestring';
    > my @stuff = ("Just", "Some", "", "", "Text");
    > my %h;@h{reverse @stuff}=reverse 0..$#stuff;
    > $h{$somestring}=$h{''};delete $h{''};
    > @stuff[values %h]=keys %h;
    > print Dumper \@stuff;
    >


    Make that:

    use warnings;
    use strict;
    use Data::Dumper;
    my $somestring='somestring';
    my @stuff = ("Just", "Some", "", "", "Text");
    my %h;@h{reverse @stuff}=reverse 0..$#stuff;
    $h{$somestring}=$h{''} if exists $h{''};delete $h{''};
    @stuff[values %h]=keys %h;
    print Dumper \@stuff;

    --
    Bob Walton
    Email: http://bwalton.com/cgi-bin/emailbob.pl
    Bob Walton, Oct 6, 2005
    #9
  10. Also sprach :

    > wrote:
    >
    > ...
    >>
    >> Something like:
    >>
    >>
    >> use List::MoreUtils qw(firstidx);
    >>
    >> my @ar = ('foo', '', 'baz', '', '?');
    >> $ar[ firstidx { defined $_ and $_ eq '' } @ar ] = 'bar';
    >>

    >
    >
    > Unfortunately, this will only work if you know
    > ahead of time that the array contains an empty
    > strings -- else I end up modifying the last
    > element of the array (because firstidx then
    > returns -1).


    I just found a nice abuse for List::MoreUtils::any(). It scans a list
    and returns true once an element meeting a criterion has been found.
    However, the items of the list can be changed. Therefore:

    use List::MoreUtils qw/any/;

    my @ary = ('foo', '', 'baz', '', '?');
    any { s/^$/string/ } @ary;
    print join ', ', @ary;
    __END__
    foo, string, baz, , ?

    Since the subsitution operator s/// returns true when a substitution was
    done this works here. Thinking about it, this is done more nicely with
    List::Util::first():

    use List::Util qw/first/;
    first { s/^$/string/ } @ary;

    Tassilo
    --
    use bigint;
    $n=71423350343770280161397026330337371139054411854220053437565440;
    $m=-8,;;$_=$n&(0xff)<<$m,,$_>>=$m,,print+chr,,while(($m+=8)<=200);
    Tassilo v. Parseval, Oct 6, 2005
    #10
  11. Dave Weaver Guest

    it_says_BALLS_on_your forehead <> wrote:
    > wrote:
    > >
    > > This almost works:
    > >
    > > do {$stuff[$_] =~ s/^$/Null/ && last} for ( 0 .. @stuff-1 ) ;
    > >

    ....
    > > my $index = 0;
    > > until ($stuff[$index] =~ s/^$/Null/ || $index >= @stuff - 1) {
    > > $index++;
    > > }
    > >
    > > It works, but... Yuck. Any ideas for a more elegant approach?

    >
    > hmm, how about:
    >
    > unless (@stuff) {
    > push( @stuff, $string );
    > }
    >
    > for (@stuff) {
    > if ($_ eq '') {
    > $_ = $string;
    > last;
    > }
    > }


    If I was maintaining code, this would be the code i'd like to see.
    Ok, it's a few more lines, but it's very easy to see what's going on.
    Dave Weaver, Oct 6, 2005
    #11
  12. John Bokma Guest

    Dave Weaver <> wrote:

    > it_says_BALLS_on_your forehead <> wrote:
    >> wrote:
    >> >
    >> > This almost works:
    >> >
    >> > do {$stuff[$_] =~ s/^$/Null/ && last} for ( 0 .. @stuff-1 ) ;
    >> >

    > ...
    >> > my $index = 0;
    >> > until ($stuff[$index] =~ s/^$/Null/ || $index >= @stuff - 1) {
    >> > $index++;
    >> > }
    >> >
    >> > It works, but... Yuck. Any ideas for a more elegant approach?

    >>
    >> hmm, how about:
    >>
    >> unless (@stuff) {
    >> push( @stuff, $string );
    >> }
    >>
    >> for (@stuff) {
    >> if ($_ eq '') {
    >> $_ = $string;
    >> last;
    >> }
    >> }

    >
    > If I was maintaining code, this would be the code i'd like to see.
    > Ok, it's a few more lines, but it's very easy to see what's going on.


    Yup, maybe "Perlified" to:

    @stuff or push @stuff, $string

    for my $item ( @stuff ) {

    $item eq '' or next;
    $item = $string;
    last;
    }


    I prefer the or stuff, since I read the first part as a requirement, and
    the second part as a "fix" for that requirement :)

    --
    John Small Perl scripts: http://johnbokma.com/perl/
    Perl programmer available: http://castleamber.com/
    I ploink googlegroups.com :)
    John Bokma, Oct 6, 2005
    #12
  13. Dr.Ruud Guest

    it_says_BALLS_on_your forehead:

    > unless (@stuff) {
    > push( @stuff, $string );
    > }
    >
    > for (@stuff) {
    > if ($_ eq '') {
    > $_ = $string;
    > last;
    > }
    > }


    Why let 'it' be tested twice?

    if (@stuff) {
    for (@stuff) {
    if ($_ eq '') {
    $_ = $string;
    last;
    }
    }
    } else {
    push( @stuff, $string );
    }


    Which screams for something like

    for (@stuff) {
    if ($_ eq '') {
    $_ = $string;
    last;
    }
    } for_else {
    push( @stuff, $string );
    }

    --
    Affijn, Ruud

    "Gewoon is een tijger."
    Dr.Ruud, Oct 6, 2005
    #13
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. David Filmer
    Replies:
    9
    Views:
    111
  2. David Filmer

    In search of elegant code: inverting a string

    David Filmer, Oct 29, 2003, in forum: Perl Misc
    Replies:
    9
    Views:
    121
    Russ Jones
    Oct 30, 2003
  3. David Filmer
    Replies:
    11
    Views:
    218
    Brad Baxter
    Dec 5, 2003
  4. David Filmer
    Replies:
    8
    Views:
    119
    geoffroy
    Dec 14, 2003
  5. David Filmer
    Replies:
    3
    Views:
    156
    Heinrich Mislik
    Sep 22, 2004
Loading...

Share This Page