Home > OS >  How to remove curlperly brackets as well from a json array of hashes
How to remove curlperly brackets as well from a json array of hashes

Time:01-17

I have an array of hashes that looks like this:

my $names = [
    {
     'name' => 'John'
    },
    {
     'name' => '$teven'
    },
    {
     'name' => 'Edgar'
    }
];

I am trying to validate it in order to remove special characters, spaces, etc. however when I delete the key, I am left with {}. For example:

foreach (@{ $names}) {
       if ($_->{name} =~ /[^\w ]/ ) {
print "Deleting $_->{name} due to non-standard characters" and delete $_->{name};
}
}

However afther that I am left with this result:

my $names = [
    {
     'name' => 'John'
    },
    {},
    {
     'name' => 'Edgar'
    }
];

Instead of just:

my $names = [
    {
     'name' => 'John'
    },
    {
     'name' => 'Edgar'
    },
];

How can I remove the extra curly brackets when deleting the key?

p.s. to clarify as I see my question has been edited, the array of hashes is exactly as I previously posted it:

{
 'name' => 'John'
}
{
 'name' => '$teven'
}
{
 'name' => 'Edgar'
}

Not with , and []; as I do a decode_json before that, so it's basically just the curly brackets that cause an issue, not the commas and square brackets.

CodePudding user response:

You are deleting from the hash when you want to remove the hash itself from the array.

When you want to filter an array, grep is your friend.

@$names =
   grep {
      if ( $_->{name} =~ /\W/ ) {
         print "Deleting $_->{name} due to non-standard characters\n";
         0
      } else {
         1
      }
   }
      @$names;

It's a bit weird to have side-effects in a grep. If you don't like it, you could also use something like the following:

my @filtered;
for ( @$names ) {
   if ( $_->{name} =~ /\W/ ) {
      print "Deleting $_->{name} due to non-standard characters\n";
   } else {
      push @filtered, $_;
   }
}

$names = \@filtered;

You could also use the less efficient @$names = @filtered; instead of $names = \@filtered; if you have other references to the array lying about.

CodePudding user response:

Deleting keys from a hash does not delete the hash itself, even if the deletion leaves the hash empty. If you want to drop empty hashes, you will have to do so explicitly. One way to do this is to put the following after your loop:

@{ $names } = grep { keys %{ $_ } } @{ $names };

The grep built-in takes either a block (as above) or an expression, plus a list. It returns only those elements for which the block (or expression) is true. The keys built-in returns the keys of the hash. In scalar context it returns the number of keys, and so will be false only if the hash has no keys. I believe that in modern Perls this operation is optimized in Boolean context so that the entire list of keys is not generated.

Yes, this assumes the contents of @{ $names } are all hash reference, but your code makes the same assumption.

It is tempting to try to do something inside the loop, but actually removing array elements while iterating over an array is a recipe for trouble. The best I could come up with inside the loop is something like $_ = undef unless keys %{ $_ };, which replaces the empty hash reference with undef -- probably not what you want.

  • Related