1

I'm working with an API to get Information and save it into a file

#!/bin/perl
use strict;
use warnings;
use JSON;
use Data::Dumper;

my %Hash;
my @Array;
my $url = "https://foo/bar"
my $apidata = `/usr/bin/curl -k -u $url`
my $apidatajson = decode_json $apidata;
my $i=0;

foreach(@{ $apidatajson->{category} }){
   @Array=();
   my $Name=$apidatajson->{category}[$i]{Name};
   my $value=$apidatajson->{category}[$i]{Value};
   
   push(@Array,($Name,$value));
   $Hash{$Name}=@Array; # Output 1
   $Hash{$Name}=\@Array; # Output 2

   $i++;

}
print Dumper \%Hash;

Output 1 looks something like this:

$VAR1{
  "FooName2" => 2;
  "FooName" => 2;
};

Output 2 looks something like this:

$VAR1{
  "FooName2" => [
               "FooName2"
               "FooValue2"
              ]
   "FooName" => $VAR1 -> {'FooName2'}
};

Id need it to look like this:

$VAR1 {
   "FooName2" => [
               "FooName2"
               "FooValue2"
                ]   
   "FooName" => [
               "FooName"
               "FooValue"
                ]
}

So essentially what I'm asking is, how can I save the content of the Array in the Hash, without using a Reference, so it doesn't mess up every entry but the last one?

2
  • You are missing a } on the line of the foreach. Futhermore, your code does not reproduce the issue you are talking about (probably because you added a my before @Array=() within the loop). Finally, you are using double quotes instead of backticks around curl https:/foo/bar. Next time, please test your code before posting a question. Commented Aug 17, 2020 at 7:49
  • 1
    (just realized my previous comment sounds a bit harsher that I would have liked): you were right to simplify your code before posting, and we appreciate. Next time, just run it locally after the simplification and before posting ;) Anyway, welcome on StackOverflow. Commented Aug 17, 2020 at 7:57

2 Answers 2

2

The code in your question is not the code you are running. In particular, I suspect that you do not have the line my @Array = () but rather @Array = () (without my).

Thus, $Hash{$Name} = \@Array; takes the reference of the array declared outside of the foreach loop (since there is actually none declared within the loop), which is why both values of your hashtable are the same.

If you declare @Array within your loop, the your code works properly.

Additional notes:

  • Your seem confused between the "foreach" loop and the C-style for loop. Your loop should be:

     for (@{ $apidatajson->{category}}){
         my @Array;
         my $Name  = $_->{Name};
         my $value = $_->{Value};
    
         push(@Array,($Name,$value));
         $Hash{$Name} = \@Array;
     }
    

    Or, since you don't really need @Array, simply:

     for (@{ $apidatajson->{category}}){
         my $Name  = $_->{Name};
         my $value = $_->{Value};
    
         $Hash{$Name} = [$Name, $value];
     }
    

    Or, if you need to keep your $i:

     for my $i (0 .. $#{$apidatajson->{category}}) {
          my $Name  = $apidatajson->{category}[$i]{Name};
          my $value = $apidatajson->{category}[$i]{Value};
    
          $Hash{$Name} = [$Name, $value];
     }
    
  • You don't need to call curl to do an http request. Instead, you can use a Perl module like LWP::Simple:

     use LWP::Simple;
     my $apidata = get("https://foo/bar") or die "Failed to get data from API!";
    
Sign up to request clarification or add additional context in comments.

3 Comments

I think you may also be confused with the distinction between for and foreach. Perl doesn't care which name you use for the loop - just what's inside the parens. The names for and foreach are interchangeable either way. Something looking like for (@list) results in a foreach loop and something like foreach (my $i = 0; $i < $n; ++$i) will result in a for loop. The difference is that foreach will implicitly localize a previously declared iteration variable, so it will revert to the value it had outside the loop, once the loop exits.
@Silvar I am well away that for and foreach are interchangeable. However, saying "you seem confused between for and for" seems less clear than "you seem confused between foreach and c-style for". Furthermore, if you look at the documentation, you'll notice that the terms "foreach" and "C-style for" are used in the same way as they are in my answer.
@Silvar Furthermore, the remark in my answer about for loops is not about "for vs foreach" but rather about the usefulness of doing my $i = 0; for (@array) { f($array[$i]); $i++ } rather than for (@array) { f($_) }. I'd say that anyone doing the former missed the point of foreach loops.
1

Try this:

$Hash{$Name}=[$Name, $value];

Square brackets are array references in Perl.

You can not avoid an array reference, if you need it. Your requirements are contradictory.

BTW: what you have created in the second version is a cyclic reference. Probably because of the same variable name in the global and local scope. But I think the output does not belong to your code.

1 Comment

yes, its the other way round so "FooName" => $VAR1 -> {'FooName2'}, if you dont mean that then maybe I missed something when simplifying my code, sorry if that is the case.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.