Perl: Rearrange array based on element value - arrays

What would be the most efficient way to rearrange the array elements so that they are ordered & place with the corresponding index value (minus one)? The $dat variable being the max number of elements (which is never exceeded in the array but may or may not be present).
$dat = 14;
my #array = (1, 12, 14, 7, 8, 4)
In other words:
my #new_array = (1, undef, undef, 4, undef, undef, 7, 8, undef, undef, undef, 12, undef, 14);
***EDIT****
Fuller code snippet:
foreach $auth (keys %activity) {
my #value = #{ $activity{$auth} };
#value = uniq #value;
#value = sort #value;
s/^0// for #value;
my $count = scalar(grep $_, #value);
my $dat = max( #value );
#{$activity{$auth}} = #value;
}

Simple and fast (duplicates may occur in the original):
my #new_array;
$new_array[$_ - 1] = $_ for #array;
In-place (duplicates not allowed in original):
for (my $i = #array; $i--; ) {
my $j = $array[$i]-1;
if ($i < $j) {
$array[$j] = $j+1;
$array[$i] = undef;
}
elsif ($i > $j) {
#array[$i, $j] = #array[$j, $i];
redo;
}
}
In-place (duplicates may occur in the original):
for (my $i = #array; $i--; ) {
my $j = $array[$i]-1;
if ($i < $j) {
$array[$j] = $j+1;
$array[$i] = undef;
}
elsif ($i > $j) {
if ($array[$j] == $j+1) {
$array[$i] = undef;
}
else {
#array[$i, $j] = #array[$j, $i];
redo;
}
}
}
pop(#array) while #array && !defined($array[-1]);

You can create an new array with all values defined after finding the maximum value in the first, then in this new array undefine any value which is not in the first:
use strict;
use warnings;
use Data::Dumper;
my #array = ('1', '4', '3');
my $max = (sort { $b <=> $a } #array)[0]; #should be 4
print Dumper(\#array);
my #new_arr;
foreach my $index (0 .. ($max - 1)) {
$new_arr[$index] = ($index + 1);
#Array should be fully populated, #new_arr = ('1', '2', '3', '4');
$new_arr[$index] = 'undef' unless (grep {$_ eq $new_arr[$index]} #array);
#Values not in the original array should be set to undef
}
# #new_arr should be (''1', 'undef', '3', '4')
print Dumper(\#new_arr);
Output:
$VAR1 = [
'1',
'4',
'3'
];
$VAR1 = [
1,
'undef',
3,
4
];

Related

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

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'
]
}
}
};

Perl - Capture sentences with occurrence of more than1 element of an array

I have a text file and a array which has a list of words. I need to find a way where I can filter out the sentences with occurrence of more than 1 . I am just not able to formulate how to write the code. Here is an example :
Input :
my #strings = (
"i'm going to find the occurrence of two words if possible",
"i'm going to find the occurrence of two words if possible",
"to find a solution to this problem",
"i will try my best for a way to this problem"
);
my #words = ("find", "two", "way");
Output :
i'm going to find the occurrence of two words if possible
i'm going to find the occurrence of two words if possible
And I do understand it's a simple problem but my mind seems to have hit a road block.
If you want strings with two or more instances of the keywords:
my #keywords = ("find", "two", "way");
my %keywords = map { $_ => 1 } #keywords;
for my $string (#strings) {
my #words = $string =~ /\w+/g;
my $count = grep { $keywords{$_} } #words; # Count words that are keywords.
if ($count >= 2) {
...
}
}
Short-circuiting alternate (i.e. good for extremely long strings):
my #keywords = ("find", "two", "way");
my %keywords = map { $_ => 1 } #keywords;
for my $string (#strings) {
my $count = 0;
while ($string =~ /\w+/g) {
if ($keywords{$_} && ++$count == 2) {
...
last;
}
}
}
If you want strings with instances of two or more keywords:
my #keywords = ("find", "two", "way");
for my $string (#strings) {
my #words = $string =~ /\w+/g;
my %seen; ++$seen{$_} for #words;
my $count = grep { $seen{$_} } #keywords; # Count keywords that were seen.
if ($count >= 2) {
...
}
}
Alternate:
my #keywords = ("find", "two", "way");
for my $string (#strings) {
my #words = $string =~ /\w+/g;
my %seen = map { $_ => -1 } #keywords;
my $count = grep { ++$seen{$_} == 0 } #words;
if ($count >= 2) {
...
}
}
Short-circuiting alternate (i.e. good for extremely long strings):
my #keywords = ("find", "two", "way");
for my $string (#strings) {
my $count = 0;
my %seen = map { $_ => -1 } #keywords;
while ($string =~ /\w+/g) {
if (++$seen{$_} == 0 && ++$count == 2) {
...
last;
}
}
}

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;

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;
}

PERL dynamically match arrays based on unique key

I'm trying to compare 2 huge arrays and want to use map. I am using unique key concept here to match the indexes.
My Arrays:
my #array1 = ( ['a','b','c','d'], ['e','f','g','h'], ['i','j','k','l'], ['m','n','o','p'], ['q','r','s','t']);
my #array2 = ( ['r','q','s','t'], ['b','a','c','d'], ['n','m','o','p'], ['f','e','g','h'], ['j','i','k','l']);
My unique Keys:
my #uk1 = (0,2,3);
my #uk2 = (1,2,3);
These arrays will be huge in size, over 30,000 indexes in each with over 20 elements in each index.
So effectively i create a map where
for ( my $j = 0; $j <= $#array1 ; $j++ )
{
my searchString;
for ( my $k = 0; $k <= $#uk1; $k++ )
{
if ( $k != 0 )
{
$searchString .= ","
}
$my searchString .= $array1[$j][$uk[$k];
}
my #result = map { $_ }
grep { join (",",$array2[$_][1],$array2[$_][2],$array2[$_][3]) ) =~ join(",",$array1[$j][0],$array1[$j][1],$array1[$j][2]) }
0 .. $#array;
}
returns matched indexes.
My problem is, how do i make this dependant on the unique keys? as the length of the unique key will keep changing and as far as i know i cannot dynamically create the $array2[$_] join part.
Hope my question is clear enough.
I want to have the logic that compares
$array1[$uk1[0]],$array1[$uk1[1]],$array1[$uk1[2]] and so on (depending on the number of keys in UK) with
$array2[$uk2[0]],$array2[$uk2[1]],$array2[$uk2[2]].......
Perhaps,
my #array1 = ( ['a','b','c','d'], ['e','f','g','h'], ['i','j','k','l'], ['m','n','o','p'], ['q','r','s','t']);
my #array2 = ( ['r','q','s','t'], ['b','a','c','d'], ['n','m','o','p'], ['f','e','g','h'], ['j','i','k','l']);
my #result;
for my $i (0 .. $#array1) {
push #result,
map { [$i, $_] }
grep {
"#{ $array1[$i] }[1,2,3]" eq "#{ $array2[$_] }[0,2,3]"
}
0 .. $#array2;
}
use Data::Dumper; print Dumper \#result;
output
$VAR1 = [
[
0,
1
],
[
1,
3
],
[
2,
4
],
[
3,
2
],
[
4,
0
]
];
What you want to use is an array slice:
But lets also make life easier:
for my $sample ( #array1 )
{
my $test= join(",", #$sample[#uk1]) ;
my #result = grep { $_ eq $test } map { join(",", #$_[#uk2] ) } #array2 ;
say "huzzah" if #result ;
}
Perl lets you specify multiple elements from an array via the "array slice":
my #list= ('a', 'b', 'c', 'd') ;
my #pieces= #list[1,3] ;

Resources