Getting an "Odd number of elements in hash assignment" error - perl

I'm getting the following error when running a Perl script
Odd number of elements in hash assignment at GenerateInterchangeFromIntegrationManifest.pl line 197.
{
"Change list" : "0"
}
This is the script:
my %labelFieldMap = (IUItemName => convertIuItemName,
Changelist => sub {},
IUItemLevel => createNormalConvert('iuItemLevel'),
ContactPOC => \&convertContacts,
Cspec => \&convertCspec,
IsNew => createBooleanConvert('isNew'),
Submitter => createNormalConvert('submitter'),
LabelType => createNormalConvert('type'),
Revision => createNestedConvert('component', 'revision'),
RevisionName => sub {},
ComponentBaseName => createNestedConvert('component', 'baseName'),
Version => createNestedConvert('component', 'version'),
PLMapping => createNormalConvert('plMapping'),
BidMapping => createNormalConvert('bidMapping'),
ClientId => createNormalConvert('clientId'),
Path => \&convertPath,
ExtendedData => \&convertExtendedData);
Can any one help me resolve this issue?

There are several subroutine calls in assignment to the hash that could be returning lists with an even number of elements (which would make the list count odd overall, and also change which data is keys and which values from that point in the list on, which is probably worse for you). As Dallaylaen has pointed out in comments, this could simply be a line which returns "nothing", return; which will evaluate to empty list (), i.e. an even length of 0, in list context. All the subroutine calls in the question code will be evaluated in list context.
I would suggest a simple debug technique:
Comment out all the lines with a function call, that should remove the warning.
Then add back a few at a time and re-test.
When the warning re-appears, you will have isolated the problem to one of a few subroutines.
Repeat until you know which one.
Then investigate that call to see how you might fix it.

Related

Verify if a user typed a word from a ReactiveList with Reactive Extension

I have a ReactiveList with keywords. The user can add or remove keyword from that list. The app needs to verify if the user typed one of the keywords.
There was already a similar post but it doesn't take in account a flexible list:
Using Reactive Extension for certain KeyPress sequences?
var keyElements = new ReactiveList<KeyElement>();
IObservable<IObservable<int>> rangeToMax = Observable.Merge(keyElements.ItemsAdded, keyElements.ItemsRemoved).Select(obs => Observable.Range(2, keyElements.Select(ke => ke.KeyTrigger.Length).Max()));
IObservable<IObservable<string>> detectedKeyTrigger = rangeToMax
.Select(n => _keyPressed.Buffer(n, 1))
.Merge().Where(m => keyElements.Where(ke => ke.KeyTrigger == m).Any());
//Here I want to end up with IObservable<string> instead of IObservable<IObservable<string>>
I can get rid of the outer IObservable by reassigning the detectedKeyTrigger each time an element in the reactive list changes, but then I lose all my subscriptions.
So, how can I end up with just an Observable of strings?
First off, both Max and Any have overloads which takes a selector and a predicate respectively. This negates the need of the Select.
Next, I changed the Observable.Merge to use the Changed property of ReactiveList which is the Rx version of INotifyCollectionChanged. I also changed the Select to produce an IEnumerable of ints instead; it just felt more Rightâ„¢.
var keyElements = new ReactiveList<KeyElement>();
IObservable<IEnumerable<int>> rangeToMax = keyElements.Changed
.Select(_ => Enumerable.Range(2, keyElements.Max(keyElement => keyElement.KeyTrigger.Length));
IObservable<IObservable<string>> detectedKeyTrigger = rangeToMax.
.Select(range => range
.Select(length => _keyPressed.Buffer(length, 1).Select(chars => new string(chars.ToArray()))) // 1
.Merge() // 2
.Where(m => keyElements.Any(ke => ke.KeyTrigger == m)) // 3
.Switch(); // 4
Create an IObservable<string> which emits the last n characters typed by the user. Create such an observable for each of the possible lengths of an combo
Merge the observables in the IEnumerable<IObservable<string>> into one Observable<string>
Only let strings which mach one of the KeyTriggers through
As rangeToMax.Select produces an IObservable<IObservable<string>> we use Switch to only subscribe to the most recent IObservable<string> the IObservable<IObservable<string>> produces.

Net::RBLClient parameter

I'm trying to pass additional RBL's to Net::RBLClient, sample code:-
use Net::RBLClient;
my $rbl = Net::RBLClient->new;
$rbl->lookup('25.23.75.65');
my #listed_by = $rbl->listed_by;
Documentation says that parameters can be passed as hash, however one of then parameter(which I'm trying to use) "lists" says it takes array reference. Couldn't understand how it exactly passed on this module.
I've a array reference like
my $rack = ['bl.spamcop.net', 'sbl.spamhaus.org', 'xbl.spamhaus.org'];
Not sure how this reference included in module construct.
Documentation:- CPAN
By the looks of the docs, the new() method accepts an optional hash as arguments, so pass in the array reference as the value to the lists key.
my $rack = ['bl.spamcop.net', 'sbl.spamhaus.org', 'xbl.spamhaus.org'];
my $rbl = Net::RBLClient->new(lists => $rack);
# then, after the object is created, carry on
$rbl->lookup('211.101.236.160');
my #listed_by = $rbl->listed_by;
You could also add other parameters in the same way if you needed/wanted to on object instantiation:
my $rbl = Net::RBLClient->new(
lists => $rack,
max_time => 10,
timeout => 3,
);
...etc. You could also declare the hash up front, and pass the whole shebang in:
my %params = (
lists => [
$blacklist_1,
$blacklist_2,
],
max_time => 10,
timeout => 3,
);
my $rbl = Net::RBLClient->new(%params);

Perl syntactical details - list context with or without parens?

This works:
my $r = someSubroutine( map { ( 0 => $_ ) } #hosts)
This does not work, giving a syntax error:
my $r = someSubroutine( map { 0 => $_ } #hosts)
What I think I understand is that the { } after the map amounts to a closure or anonymous subroutine.
But if I put a "value, value" at the end of a normal subroutine, it will return a list of those values. If I use this brevity with the map, it is a syntax error.
First of all, this is a very strange statement. The list that map produces will look like
0, $hosts[0], 0, $hosts[1], 0, $hosts[2], ...
so it's useless for assignment to a hash as it would be the same as
my %hash = (0 => $hosts[-1])
map will accept either a BLOCK (which is what you're using) or a simple EXPRESSION for its first parameter. The problem here is that { 0 => $_ } looks very like an anonymous hash with a single element, which is an EXPRESSION, and that is what the parser guesses it is. An EXPRESSION requires a comma after it, before the second parameter, but when perl gets to the closing brace in map { 0 => $_ } #hosts it doesn't find one so it has to throw a syntax error as it is too far to backtrack to the opening brace and assume a block instead
The documentation puts it like this
{ starts both hash references and blocks, so map { ... could be either the start of map BLOCK LIST or map EXPR, LIST. Because Perl doesn't look ahead for the closing } it has to take a guess at which it's dealing with based on what it finds just after the {. Usually it gets it right, but if it doesn't it won't realize something is wrong until it gets to the } and encounters the missing (or unexpected) comma. The syntax error will be reported close to the }, but you'll need to change something near the { such as using a unary + or semicolon to give Perl some help
The solution is to disambiguate it as you discovered. Any of these will work
map +( 0 => $_ ), #hosts
map(( 0 => $_ ), #hosts)
map { +0 => $_ } #hosts
map { ( 0 => $_ ) } #hosts
map { ; 0 => $_ } #hosts
map has two syntax:
map BLOCK LIST e.g. map { f() } g()
map EXPR, LIST e.g. map f(), g()
When Perl encounters map, it needs to determine which syntax was used. Let's say the first token after map is {. That's the start of a BLOCK, right? Hold on! Expressions can start with { too!
my $hash_ref = { key => 'val' };
The grammar is ambiguous. Perl has to "guess" which syntax you are using. Perl looks ahead at the next token to help guess, but sometimes it guesses incorrectly nonetheless. This is one of those cases.
The following are the standard workarounds for this:
map {; ... } LIST # Force Perl to recognize the curly as the start of a BLOCK
map +{ ... }, LIST # Force Perl to recognize the curly as the start of a hash constructor
; can't be part of a hash constructor, so the { can only start a BLOCK.
+ necessarily starts an EXPR (and not a BLOCK). It's an operator that does nothing but help in situations like this.
For example,
map {; +{ $row->{id} => $row->{val} } } #rows
This is described in perldoc on map: http://perldoc.perl.org/functions/map.html
In short you should use little helper like parens or +-symbol so perl will be able to parse {...} construct correctly:
my $r = someSubroutine( map { + 0 => $_ } #hosts)

Context and the Comma Operator

One of my colleagues used a comma after a statement instead of a semi-colon, what resulted was similar to the below code:
my $SPECIAL_FIELD = 'd';
my %FIELD_MAP = (
1 => 'a',
2 => 'b',
3 => 'c',
);
sub _translate_to_engine {
my ($href) = #_;
my %mapped_hash
= map { $FIELD_MAP{$_} => $href->{$_} } keys %$href;
$mapped_hash{$SPECIAL_FIELD} = FakeObject->new(
params => $mapped_hash{$SPECIAL_FIELD}
), # << comma here
return \%mapped_hash;
}
At first I was surprised that this passed perl -c, then I remembered the comma operator and thought I understood what was going on, but the results of the two print statements below made me doubt again.
my $scalar_return = _translate_to_engine(
{ 1 => 'uno', 2 => 'dos', 3 => 'tres' }
);
print Dumper $scalar_return;
# {'c' => 'tres','a' => 'uno','b' => 'dos','d' => bless( {}, 'FakeObject' )}
This call was made in scalar context and the result that I get is the expected result. The comma operator evaluated the LHS of the comma discarded, then evaluated the RHS. I don't believe that it can return the value of the RHS here, because evaluating the return statements leaves the subroutine.
my #list_return = _translate_to_engine(
{ 1 => 'uno', 2 => 'dos', 3 => 'tres' }
);
print Dumper \#list_return;
# [{'c' => 'tres','a' => 'uno','b' => 'dos','d' => bless( {}, 'FakeObject' )}]
This call was made in list context but the result I get is effectively the same as the call in scalar context. What I think is happening here: Both arguments are evaluated since the sub was called in list context, when the RHS is evaluated the return statement is executed so the LHS is effectively discarded.
Any clarification on the specific semantics that happen in either case would be appreciated.
Your explanation is accurate.
The context in which _translate_to_engine is called affects is the context in which all final expressions of the function are evaluated, including the argument to all return. There are two expressions affected in this case: the comma you mentioned, and \%mapped_hash.
In the first test, the returned value is \%mapped_hash evaluated in scalar context. And in the second, the returned value is \%mapped_hash evaluated in list context. \%mapped_hash evaluates to a reference to the hash, regardless of context. As such, the result of the sub is the same regardless of context.
The LHS of the expression is $mapped_hash{$SPECIAL_FIELD} = FakeObject->new(...) and the RHS is return \%mapped_hash. As you said, the comma operator evaluates the left hand side (which assigns a FakeObject instance to the hash key d), and then evaluates the right hand side, causing the sub to return the hashref. Makes sense to me.
Calling the sub in list or scalar context doesn't matter. It isn't going to change the context of the comma operator, which is the same in both cases.

Convert Hash To Array in Perl Catalyst

I need help regarding handling of Perl variables. Here I am getting input as a hash. I now need to send this hash variable to another subroutine. How can pass data as an argument to another subroutine? The code below shows how I am approaching this:
if ($csData->{'CUSTOMER_INVOICE_DETAILS'})
{
$c->log->debug("API Response:". Dumper $csData->{'CUSTOMER_INVOICE_DETAILS'});
my $Charges = [];
my #customerCharges = $csData->{'CUSTOMER_INVOICE_DETAILS'};
foreach(#customerCharges)
{
my ($customername,$customeramount) = split /:/;
my $charge_hash = ({
customername => $customername,
customeramount => $customeramount
});
push(#$Charges, $charge_hash);
}
my #ReturnCharges = $self->API->get_customer_charges($Charges, $Customer->customerid, $params->{'invoiceid'});
The other subroutine where this data is being received is as follows:
sub get_customer_charges
{
my $self = shift;
my ($charge, $CustomerId, $INID) = #_;
my $http_request = {
action => 'GetTariff',
customerid => $CustomerId,
csid => $INID,
};
my $markups = $self->APIRequest($http_request);
###Charge Level ID Inserting As 10
my #ChargeLevels;
my #BaseLevelID;
foreach my $ch (#$charge)
{
my ($customername,$customeramount) = split(':', $ch->{'customername'}, $ch->{'customername'});
my $chargelevel = join(':', $ch->{'customername'}, $ch->{'customeramount'}, '10');
push(#BaseLevelID, $chargelevel);
}
push(#ChargeLevels, #BaseLevelID);
return #ChargeLevels;
}
When I print to the server log for CUSTOMER_INVOICE_DETAILS variable I am getting the following values:
API Response:$VAR1 = {
'Product' => '34.04',
'basetax' => '2.38',
'vattax' => '4.36'
};
After sending data to second subroutine the data coming in server log for second subroutine variable is as following:
Charges in API:$VAR1 = 'HASH(0xb75d6d8)::10';
Can anyone help how could I send the hash data from one subroutine to another?
Given your comments and that your source is:
API Response:$VAR1 = {
'Product' => '34.04',
'basetax' => '2.38',
'vattax' => '4.36'
};
And you're looking for:
API Response:$VAR1 = { 34.04:2.38:4.36:10 };
(and somehow you're getting:
Charges in API:$VAR1 = 'HASH(0xb75d6d8)::10';
This suggests this may be as simple as using the values system call. values extracts an array of all the values in the hash. Something like this (guessing a bit on which part of your code needs it).
my #list_of_values = values ( %{$csData->{'CUSTOMER_INVOICE_DETAILS'}} );
You say you want to "convert" a hash to an array, but your issue seems more complex and subtle so simple conversion is not likely what will solve your problem. Something in your subroutine is returning a hash reference when the rest of your code does not expect it to do so. If the data-structure you are passing contains the correct information but not in the form you expect, then you can either change the code to produce it in the expected form (e.g. to return an ARRAY) or change your subroutine so that it is able to handle the data that it is passed correctly.
As for "converting a hash" per se, if your data structure doesn't contain complex nested references and all you want to do is "convert" your hash to an array or list, then you can simply assign the hash to an array. Perhaps I'm not understanding your question but if this kind of simple "flattening" is all you want then you could try:
my $customer_purchase = {
'Product' => '34.04',
'basetax' => '2.38',
'vattax' => '4.36'
};
my #flat_customer_purchase = %{ $customer_purchase };
say "#flat_customer_purchase" ;
Output:
basetax 2.38 Product 34.04 vattax 4.36
You can then supply the hash data as the "array" to the second subroutine. e.g. treat #flat_customer_purchase as a list:
use List::AllUtils ':all';
say join " ", pairkeys #flat_customer_purchase
# basetax Product vattax
say join " ", pairvalues #flat_customer_purchase
# 2.38 34.04 4.36
etc.
NB: this assumes that for some reason you must pass an array. The example of running the pairvalues routine simply replicates #Sobrique's suggestion to use values directly on the hash you are passing but in my answer this grabs the values pairs from the array instead of the hash.
My sense is that there is more to the question. If API Response is a more complicated hash/object or, if for some other reason this basic perl doesn't work, then you will have to supply more information. You need to find out where your unexpected hash reference is coming from before you can decide how to handle it. You might find this SO discussion helpful:
Are Perl subroutines call-by-reference or call-by-value?