How to define multidimensional arrays of different types in powershell - arrays

I'm stuck.
I would like to create a multidim array with the following structure
$x[index]['word']="house"
$x[index]['number']=2,5,7,1,9
where index is the first dimension from 0 to... n
second dimension has two fields "word" and "number"
and each of these two fields holds an array (the first with strings, the second with numbers)
I do not know how to declare this $x
I've tried with
$x = #(()),#(#()) - doesn't work
or
$x= ("word", "number"), #(#()) - doesn't work either
or
$x = #(#(#(#()))) - nope
Then I want to use this array like this:
$x[0]["word"]= "bla bla bla"
$x[0]["number]= "12301230123"
$x[1]["word"]= "lorem ipsum"
$x[2]["number]=...
$x[3]...
$x[4]...
The most frequent errors are
Array assignment failed because index '0' was out of range.
Unable to index into an object of type System.Char/INt32
I would like to accomplish this using arrays[][] or jaws # but no .net [,] stuff.
I think I'm missing something.

If I understood you correctly, you're looking for an array of hashtables. You can store whatever you want inside an object-array, so store hashtables that you can search with words or numbers as keys. Ex:
$ht1 = #{}
$ht1["myword"] = 2
$ht1["23"] = "myvalue"
$ht2 = #{}
$ht2["1"] = 12301230123
$arr = #($ht1,$ht2)
PS > $arr[1]["1"]
12301230123
PS > $arr[0]["myword"]
2
PS > $arr[0]["23"]
myvalue
If you know how many you need, you can use a shortcut to create it:
#Create array of 100 elements and initialize with hashtables
$a = [object[]](1..100)
0..($a.Length-1) | % { $a[$_] = #{ 'word' = $null; 'number' = $null } }
#Now you have an array of 100 hastables with the keys initialized. It's ready to recieve some values.
PS > $a[99]
Name Value
---- -----
number
word
And if you need to add another pair later, you can simply use:
$a += #{ 'word' = $yourwordvar; 'number' = $yournumbervar }

You could make an array, and initialize it with hashtables:
$x=#(#{})*100;
0..99 | foreach {$x[$_]=#{}};
$x[19]["word"]="house";
$x[19]["number"]=25719;
You want a big array, for example of length 100. Note the difference in parentheses!
You need the second step, because in the previous command the pointer of the hashtable was copied 100 times... and you don't want that :)
Now test it:
$x[19]["number"];
25719
$[19]["word"];
house

Related

How to edit value in a sub array

I can't understand this behavior in powershell :
$PSVersionTable.PSVersion
""
$matrix =, #(, #("foo") * 5) * 3
$matrix[0][1] = "bar"
foreach($subArray in $matrix){
Write-Host $($subArray[1])
}
With this script I'm initializing an array of 3 arrays, each containing 5 string "foo".
I want to set the 2nd element of the 1st array to "bar", and each arrays are affected :
# Returned :
Major Minor Build Revision
----- ----- ----- --------
5 1 15063 1155
bar
bar
bar
Is it a bug or am I missuntderstanding how arrays works ?
What the * operator de facto does with an array as the LHS is to use that array's elements as-is as the elements of the resulting array.
In the case at hand, this means that $matrix[0], $matrix[1], and $matrix[2] all point to the same 5-element array composed of "foo" strings, so modifying that one array's elements will be reflected in all 3 $matrix elements.
This behavior is likely by design (see bottom section), and I am unaware of a concise workaround with the * syntax.
A viable workaround is:
# Note: The (...) around the arrays to replicate aren't strictly needed,
# because `,` has higher precedence than `*`, but were added for
# readability.
$nestedArrayTemplate = (, 'foo') * 5
$matrix = (, #()) * 3 # (, $null) * 3 would work too
for ($i=0; $i -lt $matrix.Count; ++$i) { $matrix[$i] = $nestedArrayTemplate.Clone() }
As an aside: the above creates a jagged array, not a matrix (2-dimensional array). Here's how you would create a true 2-dimensional array:
$matrix = [string[,]]::new(3, 5) # PSv4-: New-Object string[] 3, 5
for ($row = 0; $row -lt $matrix.GetLength(0); ++$row) {
for ($col = 0; $col -lt $matrix.GetLength(1); ++$col ){
$matrix[$row, $col] = 'foo'
}
}
Note that indexing into this 2-dimensional array then requires a single index expression with a list of indices; e.g., [0, 1] rather than [0][1].
Design musings:
I can only speculate about the design intent, but I presume that no attempt is made to clone collections that happen to be the elements of the LHS array, because there's no guarantee that such collections can be cloned - the same more generally applies to all element types of the LHS array that are reference types (other than strings).
With LHS array elements that are value types or strings - which is the more typical case - there's no problem, because the resulting array's elements will (effectively) be independent copies.
In short: for predictable array replication with *, restrict the LHS array's element types to value types or strings.

De-reference x number of times for x number of data structures

I've come across an obstacle in one of my perl scripts that I've managed to solve, but I don't really understand why it works the way it works. I've been scouring the internet but I haven't found a proper explanation.
I have a subroutine that returns a reference to a hash of arrays. The hash keys are simple strings, and the values are references to arrays.
I print out the elements of the array associated with each key, like this
for my $job_name (keys %$build_numbers) {
print "$job_name => ";
my #array = #{#$build_numbers{$job_name}}; # line 3
for my $item ( #array ) {
print "$item \n";
}
}
While I am able to print out the keys & values, I don't really understand the syntax behind line 3.
Our data structure is as follows:
Reference to a hash whose values are references to the populated arrays.
To extract the elements of the array, we have to:
- dereference the hash reference so we can access the keys
- dereference the array reference associated to a key to extract elements.
Final question being:
When dealing with perl hashes of hashes of arrays etc; to extract the elements at the "bottom" of the respective data structure "tree" we have to dereference each level in turn to reach the original data structures, until we obtain our desired level of elements?
Hopefully somebody could help out by clarifying.
Line 3 is taking a slice of your hash reference, but it's a very strange way to do what you're trying to do because a) you normally wouldn't slice a single element and b) there's cleaner and more obvious syntax that would make your code easier to read.
If your data looks something like this:
my $data = {
foo => [0 .. 9],
bar => ['A' .. 'F'],
};
Then the correct version of your example would be:
for my $key (keys(%$data)) {
print "$key => ";
for my $val (#{$data->{$key}}) {
print "$val ";
}
print "\n";
}
Which produces:
bar => A B C D E F
foo => 0 1 2 3 4 5 6 7 8 9
If I understand your second question, the answer is that you can access precise locations of complex data structures if you use the correct syntax. For example:
print "$data->{bar}->[4]\n";
Will print E.
Additional recommended reading: perlref, perlreftut, and perldsc
Working with data structures can be hard depending on how it was made.
I am not sure if your "job" data structure is exactly this but:
#!/usr/bin/env perl
use strict;
use warnings;
use diagnostics;
my $hash_ref = {
job_one => [ 'one', 'two'],
job_two => [ '1','2'],
};
foreach my $job ( keys %{$hash_ref} ){
print " Job => $job\n";
my #array = #{$hash_ref->{$job}};
foreach my $item ( #array )
{
print "Job: $job Item $item\n";
}
}
You have an hash reference which you iterate the keys that are arrays. But each item of this array could be another reference or a simple scalar.
Basically you can work with the ref or undo the ref like you did in the first loop.
There is a piece of documentation you can check for more details here.
So answering your question:
Final question being: - When dealing with perl hashes of hashes of
arrays etc; to extract the elements at the "bottom" of the respective
data structure "tree" we have to dereference each level in turn to
reach the original data structures, until we obtain our desired level
of elements?
It depends on how your data structure was made and if you already know what you are looking for it would be simple to get the value for example:
%city_codes = (
a => 1, b => 2,
);
my $value = $city_codes{a};
Complex data structures comes with complex code.

Assign multiple local vars to array entries in perl

In Perl, I've always been confused about how to cleanly assign multiple local variables from array entries.
I use the following syntax in subs all the time, so I'm somewhat familiar with it:
my ($var1, $var2) = #_
but other variations of this confuse me. For instance, I have the following code that works:
for my $ctr (0 .. $#matchingLines) {
my $lineNo = $matchingLines[$ctr][0];
my $text = $matchingLines[$ctr][1];
Where "#matchingLines" is an array of two-element arrays.
I wish I could convert the last two lines to the obvious:
my ($lineNo, $text) = $matchingLines[$ctr];
That of course does not work. I've tried numerous variations, but I can't find anything that works.
Just dereference the array ref:
my ( $lineNo, $text ) = #{ $matchingLines[$ctr] };
Check out Perl Data Structures Cookbook for additional examples.
It sounds like you have an array of arrays. this means that the inner arrays will be array references. If you want to allocate them to vars then you need to derference them.
use strict;
use warnings;
my #matchingLines = (['y','z'],['a','b']);
for my $ctr (0 .. $#matchingLines) {
my ($lineNo, $text) = #{$matchingLines[$ctr]};
print "#Array index: $ctr - lineno=$lineNo - text=$text\n"
}
this produces the output
#Array index: 0 - lineno=y - text=z
#Array index: 1 - lineno=a - text=b

Count Perl array size

I'm trying to print out the size of my array. I've followed a few other questions like this one on Stack Overflow. However, I never get the result I want.
All I wish for in this example is for the value of 3 to be printed as I have three indexes. All I get, from both print methods is 0.
my #arr;
$arr{1} = 1;
$arr{2} = 2;
$arr{3} = 3;
my $size = #arr;
print $size; # Prints 0
print scalar #arr; # Prints 0
What am I doing wrong, and how do I get the total size of an array when declared and populated this way?
First off:
my #arr;
$arr{1} = 1;
$arr{2} = 2;
$arr{3} = 3;
is nonsense. {} is for hash keys, so you are referring to %arr not #arr. use strict; and use warnings; would have told you this, and is just one tiny fragment of why they're considered mandatory.
To count the elements in an array, merely access it in a scalar context.
print scalar #arr;
if ( $num_elements < #arr ) { do_something(); }
But you would need to change your thing to
my #arr;
$arr[1] = 1;
$arr[2] = 2;
$arr[3] = 3;
And note - the first element of your array $arr[0] would be undefined.
$VAR1 = [
undef,
1,
2,
3
];
As a result, you would get a result of 4. To get the desired 'count of elements' you would need to filter the undefined items, with something like grep:
print scalar grep {defined} #arr;
This will take #arr filter it with grep (returning 3 elements) and then take the scalar value - count of elements, in this case 3.
But normally - you wouldn't do this. It's only necessary because you're trying to insert values into specific 'slots' in your array.
What you would do more commonly, is use either a direct assignment:
my #arr = ( 1, 2, 3 );
Or:
push ( #arr, 1 );
push ( #arr, 2 );
push ( #arr, 3 );
Which inserts the values at the end of the array. You would - if explicitly iterating - go from 0..$#arr but you rarely need to do this when you can do:
foreach my $element ( #arr ) {
print $element,"\n";
}
Or you can do it with a hash:
my %arr;
$arr{1} = 1;
$arr{2} = 2;
$arr{3} = 3;
This turns your array into a set of (unordered) key-value pairs, which you can access with keys %arr and do exactly the same:
print scalar keys %arr;
if ( $elements < keys %arr ) { do_something(); }
In this latter case, your hash will be:
$VAR1 = {
'1' => 1,
'3' => 3,
'2' => 2
};
I would suggest this is bad practice - if you have ordered values, the tool for the job is the array. If you have 'key' values, a hash is probably the tool for the job still - such as a 'request ID' or similar. You can typically tell the difference by looking at how you access the data, and whether there are any gaps (including from zero).
So to answer your question as asked:
my $size = #arr;
print $size; # prints 0
print scalar #arr; # prints 0
These don't work, because you never insert any values into #arr. But you do have a hash called %arr which you created implicitly. (And again - use strict; and use warnings; would have told you this).
You are initializing a hash, not an array.
To get the "size" of your hash you can write.
my $size = keys %arr;
I just thought there should be an illustration of your code run with USUW (use strict/use warnings) and what it adds to the troubleshooting process:
use strict;
use warnings;
my #arr;
...
And when you run it:
Global symbol "%arr" requires explicit package name (did you forget to declare "my %arr"?) at - line 9.
Global symbol "%arr" requires explicit package name (did you forget to declare "my %arr"?) at - line 10.
Global symbol "%arr" requires explicit package name (did you forget to declare "my %arr"?) at - line 11.
Execution of - aborted due to compilation errors.
So USUW.
You may be thinking that you are instantiating an element of #arr when you are typing in the following code:
$arr{1} = 1;
However, you are instantiating a hash doing that. This tells me that you are not using strict or you would have an error. Instead, change to brackets, like this:
$arr[1] = 1;

Sorting Array which is a Value in A Hash in Perl

I'm trying to sort an array which is a value in a hash. The following line of code:
sort #{ $hash{$item}{'lengths'} };
produces the following error:
Useless use of sort in void context at ...
In Perl, sort does not modify the array; it returns a sorted list. You have to assign that list somewhere (either back into the original array, or somewhere else).
#{ $hash{$item}{'lengths'} } = sort #{ $hash{$item}{'lengths'} };
Or, (especially if the array is deep in a nested hash):
my $arrayref = $hash{$item}{'lengths'};
#$arrayref = sort #$arrayref;
Your original code was sorting the array, and then throwing away the sorted list, which is why it produces that warning.
Note: As salva pointed out, by default sort does a string comparison. You probably wanted a numeric sort, which you get by using sort { $a <=> $b } instead of just sort:
my $arrayref = $hash{$item}{'lengths'};
#$arrayref = sort { $a <=> $b } #$arrayref;
But that has nothing to do with the warning message you asked about.
Perl's sort does not re-order the list "in-place". It actually makes a copy of the list and then sorts and returns . so do it as cjm suggested,
you can read the similar problem on this link (see the example part)
Note also, that if you are sorting lenghts, you will have to use a numeric comparison:
my $lengths = $hash{$item}{'lengths'};
#$lengths = sort { $a <=> $b } #$lengths;
I tried something like this
my %lrn_hash;
$lrn_hash{1} = 1;
#{$lrn_hash{1}{VALS}} = (6,7,1,5,7,9);
#narr = sort #{$lrn_hash{1}{VALS}};
print "#narr\n";
and it worked fine and the output was
1 5 6 7 7 9
Wich perl version u are using ?

Resources