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

U

usenet

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?
 
I

it_says_BALLS_on_your forehead

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;
}
}
 
I

it_says_BALLS_on_your forehead

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?
 
R

robic0

an almost 1 liner:
{
if (!@stuff) { $stuff[0]='Null'; next; }
do { ($stuff[$_] =~ s/^$/Null/ && last) } for (0 .. @stuff-1);
}
 
A

attn.steven.kuo

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';
 
A

attn.steven.kuo

(e-mail address removed) 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).
 
B

Bob Walton

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;
 
B

Bob Walton

Bob said:
(e-mail address removed) 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;
 
T

Tassilo v. Parseval

Also sprach (e-mail address removed):
(e-mail address removed) 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
 
D

Dave Weaver

it_says_BALLS_on_your forehead said:
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.
 
J

John Bokma

Dave Weaver said:
it_says_BALLS_on_your forehead said:
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 :)
 
D

Dr.Ruud

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 );
}
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,900
Latest member
Nell636132

Latest Threads

Top