Sorting hash table (of hash tables) - powershell

I have hit a wall trying to get a hash table of hash tables to sort. It seems like the act of sorting is turning the hash table into something else, and I am unable to walk the new structure.
$mainHashTable = #{}
$mainHashTable.Add('B', #{'one'='B1'; 'two'='B2'; 'three'='B3'})
$mainHashTable.Add('D', #{'one'='D1'; 'two'='D2'; 'three'='D3'})
$mainHashTable.Add('A', #{'one'='A1'; 'two'='A2'; 'three'='A3'})
$mainHashTable.Add('C', #{'one'='C1'; 'two'='C2'; 'three'='C3'})
CLS
$mainHashTable
foreach ($hashtable in $mainHashTable.keys) {
foreach ($itemKey in $mainHashTable.$hashtable.keys) {
Write-Host "$itemKey $($mainHashTable.$hashtable.$itemKey)!"
}
}
Write-Host
$sortedHashTable = $mainHashTable.GetEnumerator() | sort-object -property name
$sortedHashTable
foreach ($hashtable_S in $sortedHashTable.keys) {
foreach ($itemKey_S in $sortedHashTable.$hashtable_S.keys) {
Write-Host "$itemKey_S $($sortedHashTable.$hashtable_S.$itemKey2)!"
}
}
The two lines that dump $mainHashTable & $sortedHashTable to the console look like everything is fine. But the second loop set does nothing. I have tried casting like this
$sortedHashTable = [hashtable]($mainHashTable.GetEnumerator() | sort-object -property name)
and that just throws the error
Cannot convert the "System.Object[]" value of type "System.Object[]"
to type "System.Collections.Hashtable".
So, is there some way to convert the system object to a hash table, so the loop structure works on the sorted results? Or am I better off learning to walk the structure of the System.Object? And (academically perhaps), is there a way to sort a hash table and get back a hash table?

What you're currently doing is splitting the hashtable into a list of separate entries (via .GetEnumerator()) and then sorting that - so you end up with $sortedHashTable being just an array of key/value pair objects, sorted by key, not an actual [hashtable].
is there a way to sort a hash table and get back a hash table?
No - you can't "sort a hash table" inline, because hashtables do not retain any guaranteed key order.
The way to go here is to copy the entries to an ordered dictionary, in order:
$sortedHashTable = [ordered]#{}
# Sort the keys, then insert into our ordered dictionary
foreach($key in $mainHashTable.Keys |Sort-Object){
$sortedHashTable[$key] = $mainHashTable[$key]
}

Related

How to retrieve a key in a hash table and then update the value in Powershell?

I have a hash table where keys represent email addresses and the values represent a count. The if check sees if the email address is in the hash table, if it is not contained, then add it to the hash table and increment the count.
If the email address is present in the hash table, how do I retrieve the key and then update the value counter?
Thank you!
$targeted_hash = #{}
$count = 0
foreach ($group in $targeted)
{
if (!$targeted_hash.ContainsKey('group.ManagerEmail'))
{
$targeted_hash.Add($group.ManagerEmail, $count + 1)
}
else
{
#TODO
}
}
PowerShell offers two convenient shortcuts:
assigning to an entry by key updates a preexisting entry, if present, or creates an entry for that key on demand.
using ++, the increment operator on a newly created entry implicitly defaults the value to 0 and therefore initializes the entry to 1
Therefore:
$targeted_hash = #{}
foreach ($group in $targeted)
{
$targeted_hash[$group.ManagerEmail]++
}
After the loop, the hash table will contain entries for all distinct manager email addresses containing the count of their occurrence in input array $group.

How do I get name and values from a dataset (key value pairs)?

I'm working with a system.data.dataset.
I want to loop through each row and create a psobject out of each one. The properties will be the column names. how is this possible? I want to dynamically create properties from the column names.
Assuming that you have DataRow object in $DataRow variable, you can do the following:
$DataRow.Table.Columns | ForEach-Object {$Properties=[ordered]#{}} `
{$Properties.Add($_.ColumnName, $DataRow[$_])} `
{[PSCustomObject]$Properties}
I found it easy enough to do $datarow.psobject.properties and check each property against an array of properties like "rowerror" "rowstate" to filter that junk out and at that point I can to .name .value.

How Do You Get the Name For A Specific Value In A Powershell Hash Table?

If I have a hash table like..
$theHash = #{"dog"="mean"; "cat"="nice";"bird"="annoying"}
I can get the key for a value by doing:
$theHash."dog"
How do I do the reverse? I want to put in something like, "mean" and get "dog" as the output? So if I only know "mean", how do I get dog from that?
$theHash.keys | Where-Object {$theHash["$_"] -eq 'mean'}

Best data structure for searching record

I am having a lot of IDs and I want to store them for a temporary purpose and need to search that record for some operation. Which data structure is good for this operation in Perl? Should I use a hash or an array, or is there any other module I could use to do this efficiently?
The records are 4343, 34343, 34343, 2323, 232, ....
A little more information regarding your record layout would go a long way in helping people help you. If your records are linked to id numbers then you can use a hash with the 'id' as the key and store the record as a string or an array reference as the hash value
my %records;
$records{ $id_number } = "Record for $id_number";
## OR
$records{ $id_number } = ['Record', 'for', $id_number];
This will allow you to lookup id's with complexity O(1) and easily manipulate the corresponding record.
# Assuming the records are stored in #records
for my $record (#records) {
$recStore{$record}++;
}
# To search for a record
my $recToFind = 4343;
my $recExists = $recStore{$recToFind} || 0;
The keys of the hash are the id's retrieved from your database and the corresponding values are the number of times the id was found, so for repeating records $recExists will be greater than 1, and for non-existent records it will be equal to 0. To get a list of all id's sorted numerically you could write
my #sortedID = sort {$a <=> $b} keys %records;

Perl - How to access and array element that's held within another array

Heylo again,
I've been trying to make my program somewhat easier to maintain. I have an array which I declare:
my #pizza = ($p1 = "Pizza One", $p2 = "Pizza Two" );
I then go ahead and put this #Pizza array in another array, like so:
my #food = (\#pizza);
When I attempt to access either $p1 or $p2 via the #food property I get a value returned like "Array{0x8001}" which appears to be a memory reference. What i tried was this:
$test = ${$food[$pizza[$p1]]};
What is the corret way to access this? I am looking to NOT use indexes in order to ease program readability. could you guys point me in the right direction?
Regards,
This is what I am trying to do:
I have several databases (for talks sake this is an example)
Database One
Table One (D1T1) | Column One | Column Two | Column Three | Column Four
Table Two (D1T2) | Column One | Column Two | Column Three
Database Two
Table One (D2T1) | Column One | Column Two| Column Three
Table Two (D2T2) | Column One | Column Two| Column Three
Table Three (D2T2) | Column One | Column Two| Column Three
Between these two databases there is information that is relative to particular records right across them both. What I am trying to do is create an array (each array will represent a database) and insert variables (each variable will represent a table.field. within the datasource) Once I have done this I am creating an array to hold all teh arrays (The ones that represent the db's) as this array will represent a single entry right across the two databases which I can then act on. Example:
#D1 = ( t1.col1, t1.col4, t2.col1 );
#D2 = ( t1.col1, t2.col1, t3.col2, t3.col3);
#rec = ( \#D1, \#D2 );
If I want to know what was held in Database 2 -> Table 2 -> Column 4 what would the statement be? Should I be using hashes instead? I do have a requirement for this two dimensional array/hash.
Any help is much appreciated.
MC
Not knowing what you're trying to accomplish, what data you're modeling, all I can help you with is the syntax confusion. There's confusion here on several points.
# You wrote
my #pizza = ($p1 = "Pizza One", $p2 = "Pizza Two" );
That does not do what you mean. It makes the array ("Pizza One", "Pizza Two") but it also sets $p1 to be Pizza One and $p2 to be Pizza Two. I think you're trying to assign Pizza One to the key p1. I think you want to use a hash/associated array:
# Try a hash
my %pizzas = (p1 => "Pizza One", p2 => "Pizza Two");
Now $pizzas{p1} is Pizza One. Alternatively, just make a list:
# An array
my #pizzas = ("Pizza One", "Pizza Two");
And then $pizzas[0] is Pizza One. Moving on...
# You wrote
my #food = (\#pizza);
You're trying to make another list refer to #pizza. The above makes $food[0] reference to #pizza which is not what you meant. References in Perl are scalars and have different syntax.
# What you probably meant
my $food = \#pizza;
Now $food->[0] is Pizza One and $food->[0] is Pizza Two.
# You wrote
$test = ${$food[$pizza[$p1]]};
Smacks of just adding more braces until you got output. I'd recommend reading the Perl Data Structures Cookbook. Oh, and turn on warnings. Don't turn them off until they all go away and don't turn them off because there's too many of them. Really. Really really.
Update
"I want to know what was held in Database 2 -> Table 2 -> Column 4 what would the statement be?"
Don't use numbers. Name them, because I'm sure they have names. So yes, you want hashes. So let's say Database Foo -> Table Bar -> Column Baz. You'd access that like...
$values = $databases->{Foo}{Bar}{Baz};
or long hand
$tables = $databases->{Foo};
$columns = $tables->{Bar};
$values = $columns->{Baz};
Where $values can be an array ref of all the values of Foo.Bar.Baz, or another hash keyed by the primary key of Foo.Bar. Which makes more sense depends on what you're doing to do with the data.
I'll leave it to you to adapt Chaz's answer to generate hashes instead of arrays.
Of course, I must ask why dump the whole of both databases out into a big in-memory structure and then work on it? If you have databases, query them. It'll be faster and more flexible.
We're back to that most basic of questions; what is it you're REALLY trying to do?
Based on what you described is sounds like you need something like this:
#!/usr/bin/perl
use strict;
use warnings;
my #dbs;
while (<DATA>) {
chomp;
if (/^Database/) {
push #dbs, [];
next;
}
my #table = split /\s*\|\s*/;
push #{$dbs[-1]}, [ #table[1 .. $#table] ];
}
print "datbase 1, table 2, column 1: $dbs[0][1][0]\n";
for my $db (0 .. $#dbs) {
for my $table (0 .. $#{$dbs[$db]}) {
for my $col (0 .. $#{$dbs[$db][$table]}) {
print "($db, $table, $col) is $dbs[$db][$table][$col]\n";
}
}
}
__DATA__
Database One
Table One (D1T1) | D1T1C1 | D1T1C2 | D1T1C3 | D1T1C4
Table Two (D1T2) | D1T2C1 | D1T2C2 | D1T2C3
Database Two
Table One (D2T1) | D2T1C1 | D2T1C2 | D2T1C3
Table Two (D2T2) | D2T2C1 | D2T2C2 | D2T2C3
Table Three (D2T2) | D2T3C1 | D2T3C2 | D2T3C3