Convert an array of strings into a array of arrays of strings - arrays

My goal is to convert this
my #array=("red", "blue", "green", ["purple", "orange"]);
into this
my #array = ( ["red"], ["blue"], ["green"], ["purple", "orange"]);
Current test code:
my #array = ("red", "blue", "green", ["purple", "orange"] );
foreach $item ( #array ) {
#if this was Python, it would be as simple as:
# if is not instance(item, array):
# # item wasn't a list
# item = [item]
if(ref($item) ne 'ARRAY'){
#It's an array reference...
#you can read it with $item->[1]
#or dereference it uisng #newarray = #{$item}
#print "We've got an array!!\n";
print $item, "\n";
# keep a copy of our string
$temp = $item;
# re-use the variable but convert to an empty list
#item = ();
# add the temp-copy as first list item
#item[0] = $temp;
# print each list item (should be just one item)
print "$_\n" for $item;
}else{
#not an array in any way...
print "ALREADY an array!!\n";
}
}
# EXPECTED my #array=(["red"], ["blue"], ["green"], ["purple", "orange"]);
print #array , "\n";
foreach $item (#array){
if(ref($item) ne 'ARRAY'){
#
#say for $item;
print "didn't convert properly to array\n";
}
}

The comment about python maps pretty directly to perl.
my #array = ("red", "blue", "green", ["purple", "orange"] );
foreach $item ( #array ) {
#if this was Python, it would be as simple as:
# if is not instance(item, array):
# # item wasn't a list
# item = [item]
if (ref $item ne 'ARRAY') {
$item = [ $item ];
}
}
though using map as in Borodin's answer would be more natural.

I'm wondering why you want to do this, but it's
#array = map { ref ? $_ : [ $_ ] } #array
And please don't call arrays #array; that's what the # is for.
Your comment is ridiculous
#if this was Python, it would be as simple as:
# if is not instance(item, array):
# # item wasn't a list
# item = [item]
If you were familiar with Perl then you wouldn't need to ask the question. You must be aware that there is no one-to-one translation from Python to Perl. Python is much less expressive than either Perl or C, but I can't imagine you demanding a simple conversion to C.
Please get over your bigotry.

If you push the values to a new array, you don't need to do more than evaluate whether or not $item is an arrayref:
#! perl
use strict;
use warnings;
use Data::Dumper;
my #array=("red", "blue", "green", ["purple", "orange"]);
my #new_array;
foreach my $item (#array) {
if ( ref($item) eq 'ARRAY' ) {
push #new_array, $item;
}
else {
push #new_array, [$item];
}
}
print Dumper \#new_array;
Output from Dumper:
$VAR1 = [
[
'red'
],
[
'blue'
],
[
'green'
],
[
'purple',
'orange'
]
];

After a long day of learning more Perl than I ever thought/wanted to learn... here's what I think is a workable solution:
#! perl
use strict;
use warnings;
use Data::Dumper;
my %the_dict = (duts =>
{dut_a => {UDF => 'hamnet'},
dut_b => {UDF => [ '1', '2', '3', ]},
dut_c => {UDF => [ 'Z' ], }});
print Dumper \%the_dict;
foreach my $dut (keys %{$the_dict{duts}}) {
# convert the dut's UDF value to an array if it wasn't already
if ( 'ARRAY' ne ref $the_dict{duts}->{$dut}{UDF} ) {
$the_dict{duts}->{$dut}{UDF} = [ $the_dict{duts}->{$dut}{UDF} ];
}
# now append a new value to the array
push(#{$the_dict{duts}{$dut}{UDF}}, 'works');
}
print Dumper \%the_dict;
when run we see these print-outs:
$VAR1 = {
'duts' => {
'dut_a' => {
'UDF' => 'hamnet'
},
'dut_c' => {
'UDF' => [
'Z'
]
},
'dut_b' => {
'UDF' => [
'1',
'2',
'3'
]
}
}
};
$VAR1 = {
'duts' => {
'dut_a' => {
'UDF' => [
'hamnet',
'works'
]
},
'dut_c' => {
'UDF' => [
'Z',
'works'
]
},
'dut_b' => {
'UDF' => [
'1',
'2',
'3',
'works'
]
}
}
};

Related

Iterative Hash Set up

I have the following array...
my #array=("100 2", "300 1", "200 3");
From this array I want to iteratively construct a hash.
Current Script:
my %hash;
foreach (#array) {
my #split = (split /\s+/, $_);
%hash = ("$split[0]", "$split[1]");
}
Current Output:
$VAR1 = {
'200' => '3'
};
This is not what I want. My goal is...
Goal Output:
$VAR1 = {
'100' => '2'
'300' => '1'
'200' => '3'
};
What do I need to do?
I am using: Perl 5, Version 18
Assigning to a hash—something you are doing each pass of the loop—replaces its contents. Replace
%hash = ("$split[0]", "$split[1]");
with
$hash{$split[0]} = $split[1];
Alternatively, replace everything with
my %hash = map { split } #array;

How do I breakdown common elements in hash of arrays in perl?

I am trying to find any intersections of elements within a hash of arrays in Perl
For example
my %test = (
Lot1 => [ "A","B","C"],
Lot2 => [ "A","B","C"],
Lot3 => ["C"],
Lot4 => ["E","F"],
);
The result I would be after is
Lot1 and Lot2 have AB
Lot1,Lot2 and Lot3 have C
Lot4 has E and F.
I think this could be done with a recursive function that effectively moves its way through the arrays and if an intersection between two arrays is found it calls itself recursively with the intersection found and the next array. The stopping condition would be running out of arrays.
Once the function is exited I would have to iterate through the hash to get the arrays that contain these values.
Does this sound like a good approach? I have been struggling with the code, but was going to use List::Compare to determine the intersection.
Thank you.
Array::Utils has an intersection operation where you can test the intersect of two arrays. But that's only the start point of what you're trying to do.
So I would be thinking that you need to first invert your lookup:
my %member_of;
foreach my $key ( keys %test ) {
foreach my $element ( #{$test{$key}} ) {
push ( #{$member_of{$element}}, $key );
}
}
print Dumper \%member_of;
Giving:
$VAR1 = {
'A' => [
'Lot1',
'Lot2'
],
'F' => [
'Lot4'
],
'B' => [
'Lot1',
'Lot2'
],
'E' => [
'Lot4'
],
'C' => [
'Lot1',
'Lot2',
'Lot3'
]
};
Then collapse that, into a key set:
my %new_set;
foreach my $element ( keys %member_of ) {
my $set = join( ",", #{ $member_of{$element} } );
push( #{ $new_set{$set} }, $element );
}
print Dumper \%new_set;
Giving:
$VAR1 = {
'Lot1,Lot2,Lot3' => [
'C'
],
'Lot1,Lot2' => [
'A',
'B'
],
'Lot4' => [
'E',
'F'
]
};
So overall:
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
my %test = (
Lot1 => [ "A", "B", "C" ],
Lot2 => [ "A", "B", "C" ],
Lot3 => ["C"],
Lot4 => [ "E", "F" ],
);
my %member_of;
foreach my $key ( sort keys %test ) {
foreach my $element ( #{ $test{$key} } ) {
push( #{ $member_of{$element} }, $key );
}
}
my %new_set;
foreach my $element ( sort keys %member_of ) {
my $set = join( ",", #{ $member_of{$element} } );
push( #{ $new_set{$set} }, $element );
}
foreach my $set ( sort keys %new_set ) {
print "$set contains: ", join( ",", #{ $new_set{$set} } ), "\n";
}
I don't think there's a more efficient way to tackle it, because you're comparing each array to each other array, and forming a new compound key out of it.
This gives you:
Lot1,Lot2 contains: A,B
Lot1,Lot2,Lot3 contains: C
Lot4 contains: E,F
This can be done as two simple hash conversions:
Build a hash that lists all of the lots each item is in
Convert that to a hash that lists all items for each lot combination
Then just dump the last hash in a convenient form
This is the code.
use strict;
use warnings 'all';
use feature 'say';
my %test = (
Lot1 => [ "A", "B", "C" ],
Lot2 => [ "A", "B", "C" ],
Lot3 => ["C"],
Lot4 => [ "E", "F" ],
);
my %items;
for my $lot ( keys %test ) {
for my $item ( #{ $test{$lot} } ) {
push #{ $items{$item} }, $lot;
}
}
my %lots;
for my $item ( keys %items ) {
my $lots = join '!', sort #{ $items{$item} };
push #{ $lots{$lots} }, $item;
}
for my $lots ( sort keys %lots ) {
my #lots = split /!/, $lots;
my $items = join '', #{ $lots{$lots} };
$lots = join ', ', #lots;
$lots =~ s/.*\K,/ and/;
printf "%s %s %s\n", $lots, #lots > 1 ? 'have' : 'has', $items;
}
output
Lot1 and Lot2 have AB
Lot1, Lot2 and Lot3 have C
Lot4 has EF
It generates an %items hash that looks like this
{
A => ["Lot2", "Lot1"],
B => ["Lot2", "Lot1"],
C => ["Lot2", "Lot3", "Lot1"],
E => ["Lot4"],
F => ["Lot4"],
}
and from that a %lots hash that looks like this
{
"Lot1!Lot2" => ["A", "B"],
"Lot1!Lot2!Lot3" => ["C"],
"Lot4" => ["E", "F"],
}

Perl multi hash from list

I have a list with some values which are connected. I need to create a hashmap with keys and values from the list and merge together. But i don't really know how to do it.
Input:
my #in =(
'mgenv/1_2_3/parent.dx_environment',
'mgenv/1_2_3/doc/types.dat');
Expected output:
"{ $env => { $ver => [ $file1, $file2, ... ] } }"
I've tried these:
(1)
my #sack_files = (
'mgenv/1_2_3/parent.dx_environment',
'mgenv/1_2_3/doc/types.dat');
my $sack_tree = {};
my %hash=();
for( my $i=0; $i<scalar #sack_files; $i++){
my #array = split(/[\/]+/,$sack_files[$i]);
for(my $i=0;$i<(scalar #array)-1;$i++){
my $first = $array[$i];
my $second = $array[$i+1];
$hash{$first}=$second;
}
# merge
}
(2)
use Data::Dumper;
my #sack_files = (
'mgenv/1_2_3/parent.dx_environment',
'mgenv/1_2_3/doc/types.dat',
);
my $sack_tree = {};
my %hash=();
for( my $i=0; $i<scalar #sack_files; $i++){
my #array = split(/[\/]+/,$sack_files[$i]);
nest(\%hash,#array);
}
In the second case I get an error because when the loop variable i=1 ,the key/values already exists so maybe i have to check the previously added key/values. But I don't really know how.
I would really appreciate any ideas.
Just use push to add new members to an existing array in a hash of hashes. You have to dereference the array reference with #{ ... }.
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
my #sack_files = qw( mgenv/1_2_3/parent.dx_environment
mgenv/1_2_3/doc/types.dat
mgenv/1_2_3/doc/etc.dat
mgenv/4_5_6/parent.dx_environment
mgenv/4_5_6/doc/types.dat
u5env/1_2_3/parent.dx_environment
u5env/1_2_3/doc/types.dat
u5env/4_5_6/parent.dx_environment
u5env/4_5_6/doc/types.dat
);
my %hash;
for my $sack_file (#sack_files) {
my ($env, $ver, $file) = split m{/}, $sack_file, 3;
push #{ $hash{$env}{$ver} }, $file;
}
print Dumper \%hash;
output
$VAR1 = {
'mgenv' => {
'1_2_3' => [
'parent.dx_environment',
'doc/types.dat',
'doc/etc.dat'
],
'4_5_6' => [
'parent.dx_environment',
'doc/types.dat'
]
},
'u5env' => {
'4_5_6' => [
'parent.dx_environment',
'doc/types.dat'
],
'1_2_3' => [
'parent.dx_environment',
'doc/types.dat'
]
}
};

perl hash with array

I did same hash like this:
my %tags_hash;
Then I iterate some map and add value into #tags_hash:
if (#tagslist) {
for (my $i = 0; $i <= $#tagslist; $i++) {
my %tag = %{$tagslist[$i]};
$tags_hash{$tag{'refid'}} = $tag{'name'};
}}
But I would like to have has with array, so when key exists then add value to array.
Something like this:
e.g. of iterations
1,
key = 1
value = "good"
{1:['good']}
2,
key = 1
value = "bad"
{1:['good', 'bad']}
3,
key = 2
value = "bad"
{1:['good', 'bad'], 2:['bad']}
And then I want to get array from the key:
print $tags_hash{'1'};
Returns: ['good', 'bad']
An extended example:
#!/usr/bin/perl
use strict;
use warnings;
my $hash = {}; # hash ref
#populate hash
push #{ $hash->{1} }, 'good';
push #{ $hash->{1} }, 'bad';
push #{ $hash->{2} }, 'bad';
my #keys = keys %{ $hash }; # get hash keys
foreach my $key (#keys) { # crawl through hash
print "$key: ";
my #list = #{$hash->{$key}}; # get list associate within this key
foreach my $item (#list) { # iterate through items
print "$item ";
}
print "\n";
}
output:
1: good bad
2: bad
So the value of the hash element to be an array ref. Once you have that, all you need to do is push the value onto the array.
$hash{$key} //= [];
push #{ $hash{$key} }, $val;
Or the following:
push #{ $hash{$key} //= [] }, $val;
Or, thanks to autovivification, just the following:
push #{ $hash{$key} }, $val;
For example,
for (
[ 1, 'good' ],
[ 1, 'bad' ],
[ 2, 'bad' ],
) {
my ($key, $val) = #$_;
push #{ $hash{$key} }, $val;
}

How do I iterate through a reference to an array of hashes in Perl?

I have a reference to an array of hases that I pass to a subroutine in my perl script
This is the code:
sub mySub {
(my $resultref) = #_;
my #list = #$resultref;
print Dumper(#list);
foreach my $result (#list) {
print Dumper($result);
}
}
And this is the output:
$VAR1 = [
{
'portName' => '1.1',
'ips' => [
'192.168.1.242'
],
'switchIp' => '192.168.1.20',
'macs' => [
'00:16:76:9e:63:47'
]
},
{
'portName' => '1.10',
'ips' => [
'192.168.1.119',
'192.168.1.3'
],
'switchIp' => '192.168.1.20',
'macs' => [
'd0:67:e5:f8:7e:7e',
'd0:67:e5:f8:7e:76'
]
},
];
$VAR1 = [
{
'portName' => '1.1',
'ips' => [
'192.168.1.242'
],
'switchIp' => '192.168.1.20',
'macs' => [
'00:16:76:9e:63:47'
]
},
{
'portName' => '1.10',
'ips' => [
'192.168.1.119',
'192.168.1.3'
],
'switchIp' => '192.168.1.20',
'macs' => [
'd0:67:e5:f8:7e:7e',
'd0:67:e5:f8:7e:76'
]
},
];
The loop is putting the whole array into the $result variable. I have tried dereferencing it as #$result[0] with no success.
How do I loop those hashes individually?
Thanks!
The arguments to Data::Dumper's Dumper function should be references. E.g.:
use Data::Dumper;
my #array = ([1,2,3], [11,22,33]); # Two-dimensional array
print Dumper #array; # print array
print Dumper \#array; # print reference to array
The output:
$VAR1 = [
1,
2,
3
];
$VAR2 = [
11,
22,
33
];
$VAR1 = [
[
1,
2,
3
],
[
11,
22,
33
]
];
The second print gives us the entire structure in one variable. When you print the array directly, it expands into all its elements, so...
print Dumper #array;
Is equivalent to:
print Dumper $array[0], $array[1], ..., $array[$#array];
So, in your case, just do:
sub mySub {
my ($resultref) = #_;
print Dumper $resultref;
}
Accessing the inner variables:
Just take a look at Data::Dumper's output:
$VAR1 = [ # bracket denotes start of an array ref
{ # curly brackets = hash ref
'portName' => '1.1',
'ips' => [
'192.168.1.242'
],
'switchIp' => '192.168.1.20',
'macs' => [
'00:16:76:9e:63:47'
]
}, # hash ref ends, comma = new array element begins
{ # new hash ref
'portName' => '1.10',
'ips' => [
'192.168.1.119',
'192.168.1.3'
],
'switchIp' => '192.168.1.20',
'macs' => [
'd0:67:e5:f8:7e:7e',
'd0:67:e5:f8:7e:76'
]
}, # end of hash
]; # end of array
Important to note here is that all elements of an array, and all the values of a hash are scalars. Therefore, all hashes and arrays can easily be broken up into a list of scalars.
for my $aref (#$resultref) { # starting array ref
for my $aref2 (#$aref) { # second level array ref
for my $href (#$aref2) # here begins the hash
local $\ = "\n"; # add newline to print for simplicity
print $href->{portName}; # printing a scalar
print for #{$href_>{ips}}; # printing an array ref w post-script loop
print $href->{switchIp};
print for #{$href->{macs}};
}
}
}
Note the use of the arrow operator to dereference a reference. If you have a hash or array you would do $array[0] or $hash{$key}, but by using a reference, you "point" to the address contained in the reference instead: $array->[0] or $hash->{$key}.
The parameter passed to mySub is a reference to an array of arrayrefs. To iterate over the nested arrays you could do:
sub mySub {
my ($resultref) = #_;
for my $result (#$resultref) {
my #list = #$result; # array of hashrefs
...
}
}
I have a reference to an array of hases
No, you've passed in a reference to an array of references to an array of hashes.
If you remove that outer level of indirection then your code works as desired.

Resources