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

Dear Monks

I've this array that I need to copy to a hash, like:
%b = map{ $_, 1 } @a ;
This works great. But now I first delete an element from @a. The result is that I endup with and hash with one key == undef. How do I change this expression so undef values are ignored by map ??

Thanks in advance
Luca

Replies are listed 'Best First'.
Re: from array to hash
by ikegami (Patriarch) on Mar 09, 2006 at 15:16 UTC
    %b = map { $_ => 1 } grep defined, @a;

    or

    %b = map { defined ? ($_ => 1) : () } @a;

    or you could avoid getting undef in the first place by using splice instead of delete or undef to delete the element from the array:

    @a = qw( a b c d ); # Delete 1 starting at index 2. # What used to be at index 3 is now at index 2. splice(@a, 2, 1); print("\@a has ", scalar(@a), "elements.\n");
      Thanks, but what if the hash already has some values, like:
      #! /usr/bin/perl @a = qw(a b c d ); $b{x} = 10 ; %b = map { $_, 1} splice(@a, 0, scalar(@a) ); print "$_ --> $b{$_}\n" foreach (keys %b) ;
      all values of %b are erased using map.....
      Furthermore, is scalar(@a) really necessary, or can you as well do @a ?

      Luca
        In that case, you can use a loop instead:

        $b{$_} = 1 for grep defined, @a;

        You've severly misunderstood the use of splice. Did you read the docs?

        @a = qw(a b c d ); undef $a[2]; $b{x} = 10 ; $b{$_} = 1 foreach grep defined, @a;

        or

        @a = qw(a b c d ); splice(@a, 2, 1); $b{x} = 10 ; $b{$_} = 1 foreach @a;
Re: from array to hash
by radiantmatrix (Parson) on Mar 09, 2006 at 16:04 UTC

    Maybe map isn't what you want:

    my %b; foreach (@a) { $b{$_} = 1 if defined $_ }

    Using foreach is probably faster anyhow. FWIW:

    use Benchmark ':all'; my @a; push @a, rand(500) for (0..1000); #build array $a[int(rand(1000))] = undef; #delete an element (sort of) $a[int(rand(1000))] = undef; #delete an element (sort of) cmpthese( 1_000, { 'map' => sub { my %b = map{ $_ => 1 } @a }, 'foreach' => sub { my %b; foreach(@a) { $b{$_} = 1 if defined $_ } + }, }); __END__ Rate map foreach map 374/s -- -44% foreach 673/s 80% --

    Note: with warnings on, the map is even slower, because a warning is generated every time an undefined value is encountered in the array. I turned warnings off after discovering this, and so the numbers above represent a sort of best-case.

    <-radiant.matrix->
    A collection of thoughts and links from the minds of geeks
    The Code that can be seen is not the true Code
    I haven't found a problem yet that can't be solved by a well-placed trebuchet
Re: from array to hash
by linux454 (Pilgrim) on Mar 09, 2006 at 17:16 UTC
    Like previous posters have said use splice(@arr, $off, $len) to delete items from your lists, though if you can't use slice then you will have to test for definedness yourself. Example:
    my @a = qw( a b c d ); # delete a & c splice(@a, 0, 1) splice(@1, 1, 1)
    As for converting the array into a hash while preserving the previous contents something like this will do the job nicely:
    my @a = qw( a b c d ); my %b = ( 'e' => 2 ); %b = (%b, map { $_ => 1} grep defined, @a);
    This preserves the previous contents of %b while adding keys for @a into %b.