powershell double for loop - powershell

Need advice on loop
$Variable contains 11111 22222
foreach ($variable in $value) {
for ([byte]$c = [char]'b'; $c -le [char]'c'; $c++) {
$variable."([char]$c)" } }
I am looking output as 11111b and then 22222c but currently, I am getting 11111b , 11111c and then 22222b and then 22222c.
Kindly advice

I am assuming you mean that $value, not $variable, contains 11111 and 22222, specifically in an array.
Since you want $c to maintain its value between iterations of the foreach loop you need to initialize $c outside of the foreach loop. Therefore, you really don't need (or, rather, should not use) two loops at all.
$value = 11111, 22222;
[Byte] $c = [Char] 'b';
foreach ($variable in $value)
{
"$variable$([Char] $c++)"
}
This gives the output you are seeking:
11111b
22222c

Related

Concatenate Arrays as String with PowerShell

i have two array ($a , $b) which holds the following strings:
$a
MSSQL11.SQLINST15
MSSQL12.SQLINST16
MSSQL12.SQLINST17
$b
2874
2884
2885
That i'm trying to concatenate to a new array ($c), as a strings with a comma sign in the middle:
$c
MSSQL11.SQLINST15,2874
MSSQL12.SQLINST16,2884
MSSQL12.SQLINST17,2885
Problem is, that using my current ps code:
$c = #($a + ',' + $b)
[string]::Concat($c)
$c
I'm getting a very strange output:
MSSQL11.SQLINST15
MSSQL12.SQLINST16
MSSQL12.SQLINST17
,
2887
2884
2885
At the end, i've use a powershell hashtable.
I like this way of doing it. Create a zip function to combine your two arrays into a single array of tuples.
Then it's just a matter of piping piece-wise over that tuple list our zip function gives us. Super convenient and reusable.
function Zip($a, $b) {
while ($a) {
$x, $a = $a
$y, $b = $b
[tuple]::Create($x, $y)
}
}
$c = zip $a $b |% {$_.item1 + [string]$_.item2}
Note you can choose where ya wanna to your [string] conversion (in the following, the last line of zip).
function Zip($a, $b) {
while ($a) {
$x, $a = $a
$y, $b = $b
[tuple]::Create([string]$x, $y)
}
}
$c = zip $a $b |% {$_.item1 + $_.item2}
Extra info: Looks like you're accidentally just combining the two arrays using a ",". But $a and $b are arrays, so what you actually want to do is combine the first element of $a with the first element of $b (aka $a[0] + [string]$b[0], and so on for each element. Why the [0]? Remember we almost always start counting at 0 in programming. So the second item of an array is actually [1].
Edit: Here is an example using a foreach loop.
```
foreach($item in $a) {
$i = $a.IndexOf($item)
$a[$i] + [string]$b[$i]
}
```
If for some reason $a has 10 things in it, and $b only has 2 things, it's gonna give you funny behavior. So be careful there. Please let me know if I can better clarify anything. Don't forget to experiment.

How to access a local variable with in a foreach loop outside of its scope?

I have two arrays of string data type and i am comparing those using foreach loops and raising the counter when a match is found
my #array1 = ('India');
my #array2 = ('India', 'America');
foreach my $a (#array1) {
foreach my $b (#array2) {
my $count=0;
if($a eq $b) {
$count++;
}
}
}
Now I want to use this count variable outside of its scope
if ($count > 0) {
call_some_function();
}
Where am I going wrong?
$count is declared into the foreach loop, it doesn't exist outside this loop, if you want to use $count outside the loop, simply put my $count=0 before the first foreach and remove the one into the foreach
here's what you want:
my #array1=('India');
my #array2=('India','America');
my $count=0;
foreach my $country1(#array1)
{
foreach my $country2(#array2)
{
if($country1 eq $country2)
{
$count++;
}
}
}
Declare variable outside of the loops, you've also got a typo in $count:
my $count = 0;
foreach $a (#array1) {
foreach $b (#array2) {
if ( $a eq $b ) {
$count++;
}
}
}

Perl: How to access an array thats inside of 3 hashes passed to subroutine by reference

I have a code that is something like this:
foreach $item (#total_data)
{
setinfo($item);
} # #total_data contains an array of references to hashes (\%hash1 ... \%hashN)
In the subrutine goes something like this:
sub setinfo
{
my ($argument) = #_;
my $i = 0;
#inside original hash $argument{"data"}{"fulldraw"} there is an [array]
#that contains numbers of the form XYYZ and I want to split them into
#the following pairs XY YY YZ but that code works ok#
foreach $item (${$argument{"data"}{"fulldraw"}})
{
my $match;
my $matchedstr;
if ($item =~ /^\d{4}$/)
{
...
}
else
{
print STDERR "DISCARDED: $item\n";
}
}
}
I know I am probably making the mistake in how I am dereferencing it, but couldn't figure it out with all the articles I've read on the internet.
Thanks!
#{ ... } # dereference
Maybe $argument is a hashref; you need to use
foreach $item (#{ $argument->{data}->{fulldraw} })
Just use dereference #{ ... }:
foreach $item (#{ $argument->{data}{fulldraw} })
You can use Data::Dumper to visualize complex structures:
use Data::Dumper;
print Dumper($argument);

Yaml input perl eternal looping issue

$description is Input from a yaml file of format
main_key:
-
key1:value2
key2:value2
-
key1:value1
key2:value2
Basically that is a hash of array of hashes.
I input $description and process the inner hash as follows:
while ( my ( $mod, $defined ) = each %{ $description } ) {
my $index = 0;
foreach ( #{ $defined } ) {
while ( my ( $key, $value ) = each %{ $_ } ) {
process ( $key, $mod, $description, $index );
}
$index = $index + 1;
}
}
When certain 'keyword' is used as a key I replace add more key,value pairs to the inner hash
function1() and function2() return a hash pointer.
sub process {
my ( $key, $mod, $description, $index ) = #_;
my $parameters;
if ( $key eq 'keyword' ) {
$parameters = function1( );
}
else {
$parameters = function2( );
}
$description->{$mod}[$index] = { %$parameters, %{$description->{$mod}[$index]} };
}
The issue here is that "while ( my ( $key, $value ) = each %{ $_ } )" in the main code runs forever, using the same key and value over and over again.
Yeah. Don't do that.
Never modify a hash while looping over it. From perldoc -f each:
If you add or delete a hash's elements while iterating over it,
entries may be skipped or duplicated--so don't do that.
The general pattern is to build up the list of modifications, and then make them after the end of the loop. You can, of course, embed that sequence in an outer loop that iterates over the hash until there are no more modifications that need to be made.
This refactoring of your code works fine. I have rewritten process to do all that is necessary for the innermost hashes. I have named these $item as I don't know what they are supposed to represent. Please amend this to something more descriptive.
There never was any reason to pass all those parameters, as the values of $description, $mod, and $index were only used to locate the hash in question using $description->{$mod}[$index] so it may as well have been passed directly as a reference, which is what I do. In addition, because process now loops over the array contents there is no need to pass $key either, so the subroutine now has just one parameter.
Each element of $item is examined, and the new hash of data to be added for that element is obtained from function1 or function2 as appropriate and pushed onto #params instead of being inserted straight away.
Once all the new values have been established, they are all added into $item and the process is complete.
for my $defined (values %$description) {
process($_) for #$defined;
}
sub process {
my ($item) = #_;
my #params;
for my $key (keys %$item) {
push #params, $key eq 'keyword' ? function1() : function2();
}
for my $params (#params) {
#{$item}{keys %$params} = values %{$params};
}
}

Perl IF statement not matching variables in REGEX

my $pointer = 0;
foreach (#new1)
{
my $test = $_;
foreach (#chk)
{
my $check = $_;
chomp $check;
delete($new1[$pointer]) if ($test =~ /^$check/i);
}
$pointer++;
}
The if statement never matches the fact that many entries in the #new1 array do contain $check at the start of the array element (88 at least).
I am not sure it is the nested loop that is causing the problem because if i try this it also fails to match:
foreach (#chk)
{
#final = (grep /^$_/, #new1);
}
#final is empty but I know at least 88 entires for $_ are in #new1.
I wrote this code on a machine running Windows ActivePerl 5.14.2 and the top code works. I then (using a copy of #new1) compare the two and remove any duplicates (also works on 5.14.2). I did try to negate the if match but that seemed to wipe out the #new1 array (so that I didn't need to do a hash compare).
When I try to run this code on a Linux RedHat box with Perl 5.8.0 it seems to struggle with the variable matching in the REGEX. If I hard code the REGEX with an example I know is in #new1 the match works and in the first code the entry is deleted (in the second one value is inserted in #final).
The #chk array is a listing file on the web server and the #new1 array is created by opening two log files on the web server and then pushing one into the other.
I had even gone to the trouble of printing out $test and $check in each loop iteration and manually checking to see if any of the the values did match and some of them do.
It has had me baffled for days now and I have had to throw the towel in and ask for help, any ideas?
As tested by user1568538, the solution was to replace
chomp $check;
with
$check =~ s/\r\n//g;
to remove Windows-style line endings from the variable.
Since chomp removes the contents of the input record separator $/ from the end of its argument, you could also change its value:
my $pointer = 0;
foreach (#new1)
{
my $test = $_;
foreach (#chk)
{
local $/="\r\n";
my $check = $_;
chomp $check;
delete($new1[$pointer]) if ($test =~ /^$_/i);
}
$pointer++;
}
However, since $/ also affects other operations (such as reading from a file handle), perhaps it is safest to avoid changing $/ unless you are sure if it is safe. Here I limit the change to the foreach loop where the chomp occurs.
No knowing what your input data looks like, using \Q might help:
if ($test =~ /^\Q$check/i);
See quotemeta.
It is not clear what you are trying to do. However, you may be trying to only get those elements for which there is no match or vice versa. Adapt the code below for your needs
#!/usr/bin/perl
use strict; use warnings;
my #item = qw(...); # your #new?
my #check = qw(...); # your #chk?
my #match;
my #nomatch;
ITEM:
foreach my $item (#item) {
CHECK:
foreach my $check (#check) {
# uncomment this if $check should not be interpreted as a pattern,
# but as literal characters:
# $item = '\Q' . $item;
if ($item =~ /^$check/) {
push #match, $item;
next ITEM; # there was a match, so this $item is burnt
# we don't need to test against other $checks.
}
}
# there was no match, so lets store it:
push #nomatch, $item.
}
print "matched $_\n" for #matched;
print "didn't match $_" for #nomatch;
Your code is somewhat difficult to read. Let me tell you what this
foreach (#chk) {
#final = (grep /^$_/, #new1);
}
does: It is roughly equivalent to
my #final = ();
foreach my $check (#chk) {
#final = grep /^$check/, #new1;
}
which is equivalent to
my #final = ();
foreach my $check (#chk) {
# #final = grep /^$check/, #new1;
#final = ();
foreach (#new) {
if (/^$check/) {
push #final, $_;
last;
}
}
}
So your #final array gets reset, possibly emptied.