oneliner - clean hash with getenumerator

%3CLINGO-SUB%20id%3D%22lingo-sub-3482028%22%20slang%3D%22en-US%22%3Eoneliner%20-%20clean%20hash%20with%20getenumerator%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-3482028%22%20slang%3D%22en-US%22%3E%3CP%3EHi%2C%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3Eis%20there%20any%20way%20to%20do%20this%20in%20a%20oneliner%20manner%20%3F%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-powershell%22%3E%3CCODE%3E%23Removing%20records%20older%20than%20365%20days%0A%24tmp%20%3D%20%24IPRouteInformation.clone()%0Aforeach(%24k%20in%20%24tmp.GetEnumerator())%7B%0A%20%20%20%20if(%24k.Value.TimeID%20-lt%20(date%20(get-date).AddDays(-365)%20-Format%20yyyyMMddhhmmssfff))%7B%0A%20%20%20%20%20%20%20%20%24IPRouteInformation.Remove(%24k.Key)%0A%20%20%20%20%7D%0A%7D%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EI%20tried%20different%20things%20but%20seems%20like%20get-enumerator%20is%20blocking%20me.%20The%20code%20bellow%20does%20nothing.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-powershell%22%3E%3CCODE%3E%24IPRouteInformation.count%0A%24IPRouteInformation.GetEnumerator()%20%7C%20%3F%20%7B%20%24_.Value.TimeID%20-lt%20(date%20(get-date).AddDays(-1)%20-Format%20yyyyMMddhhmmssfff)%20%7D%20%7C%20%25%20%7B%24IPRouteInformation.Remove(%24_)%7D%0A%24IPRouteInformation.count%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EThank%20you%20for%20your%20help.%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-LABS%20id%3D%22lingo-labs-3482028%22%20slang%3D%22en-US%22%3E%3CLINGO-LABEL%3EPowerShell%3C%2FLINGO-LABEL%3E%3C%2FLINGO-LABS%3E%3CLINGO-SUB%20id%3D%22lingo-sub-3509979%22%20slang%3D%22en-US%22%3ERe%3A%20oneliner%20-%20clean%20hash%20with%20getenumerator%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-3509979%22%20slang%3D%22en-US%22%3E%3CP%3E%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F1249843%22%20target%3D%22_blank%22%3E%40John_Dodo%3C%2FA%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EHashtables%20aren't%20great%20for%20this%20kind%20of%20thing.%20It'd%20be%20easier%20and%20far%20more%20efficient%20with%20a%20list%20than%20a%20dictionary%2C%20but%20I'll%20still%20speak%20to%20the%20request%20above.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EAs%20you've%20already%20noted%2C%20one%20option%20is%20to%20call%20.Clone()%20and%20then%20look%20to%20use%20.Remove()%20to%20remove%20items%20you%20don't.%20The%20problem%20with%20this%20approach%20is%20it%20doesn't%20scale%20well%20since%20you%20have%20to%20perform%20a%20full%20duplication%20of%20the%20original%20list's%20references%20(i.e.%20a%20shallow%20copy.)%26nbsp%3BIf%20the%20original%20list%20is%20very%20large%2C%20this%20can%20prove%20quite%20inefficient.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EConversely%2C%20if%20you%20expect%20that%20the%20final%20list%20will%20be%20roughly%20the%20same%20size%20as%20the%20initial%20list%20(i.e.%20you're%20not%20filtering%20out%20all%20that%20much)%20then%20any%20efficiency%20gains%20you%20might%20achieve%20using%20another%20approach%20are%20offset%20by%20how%20easy%20using%20.Clone()%20makes%20things.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EHowever%2C%20if%20you're%20expecting%20the%20final%20list%20to%20be%20much%20smaller%20(a%20subjective%20statement%2C%20but%20let's%20say%20less%20than%20half)%2C%20then%20you'd%20be%20better%20off%20creating%20an%20empty%20second%20hashtable%20and%20calling%20the%20.Add()%20method%20on%20it%20to%20add%20just%20the%20records%20you%20wish%20to%20keep.%20This%20approach%20avoids%20a%20full%20duplication%20of%20the%20original%20list.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3ENeither%20approach%20is%20as%20efficient%20as%20using%20a%20list%20but%20it%20illustrates%20that%20there%20are%20options%20even%20within%20the%20hashtable%20context.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-powershell%22%3E%3CCODE%3E%23%20Example%201%3A%20Growing%20a%20new%20list%20(potentially%20more%20efficient%20-%20depends%20on%20size%20of%20original%20collection.)%0A%24FinalList%20%3D%20%40%7B%7D%3B%0A%24Ticks%20%3D%20%5Bdatetime%5D%3A%3ANow.AddDays(-365).Ticks%3B%0A%24IPRouteInformation.GetEnumerator()%20%7C%20ForEach-Object%20%7B%20if%20(%24_.Value.TimeID.Ticks%20-ge%20%24Ticks)%20%7B%20%24FinalList.Add(%24_)%3B%20%7D%20%7D%0A%0A%23%20Example%202%3A%20Fully-duplicating%20the%20original%20list%20then%20removing%20entries%20(i.e.%20shrinking%20the%20new%20list).%0A%24Ticks%20%3D%20%5Bdatetime%5D%3A%3ANow.AddDays(-365).Ticks%3B%0A%24IPRouteInformation.Clone().GetEnumerator()%20%7C%20ForEach-Object%20%7B%20if%20(%24_.Value.TimeID.Ticks%20-lt%20%24Ticks)%20%7B%20%24IPRouteInformation.Remove(%24_.Key)%3B%20%7D%20%7D%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EI've%20assumed%20from%20your%20code%20that%20%22TimeID%22%20is%20a%20%5Bdatetime%5D%20object.%20If%20it's%20a%20string%2C%20let%20me%20know%20and%20I'll%20change%20my%20examples%20to%20suit%20if%20you%20need%20me%20to.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3ECheers%2C%3C%2FP%3E%3CP%3ELain%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-3510055%22%20slang%3D%22en-US%22%3ERe%3A%20oneliner%20-%20clean%20hash%20with%20getenumerator%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-3510055%22%20slang%3D%22en-US%22%3E%3CP%3E%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F314983%22%20target%3D%22_blank%22%3E%40LainRobertson%3C%2FA%3E%26nbsp%3Bthank%20you%20very%20much%2C%20very%20useful%20as%20usual.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EI%20will%20review%20these%20lines%20but%20I%20already%20know%20that%20it's%20going%20to%20work.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EFinally%20what%20I%20really%20understand%20is%20that%20I%20need%20to%20learn%20what%20a%20list%20is%20(never%20used%20it)%20and%20how%20it%20compares%20to%20arrays%20and%20hashtables%2C%20and%20consequently%2C%20when%20we%20do%20need%20arrays%2C%20as%20hastables%20and%2For%20list%20seem%20to%20be%20much%20more%20convenient.%3C%2FP%3E%3CP%3EAlso%20I%20quite%20often%20do%20hash%20of%20hashes%20variables.%20I%20wonder%20if%20the%20right%20answer%20is%20not%20a%20list%20of%20hashes.%20To%20be%20studied...%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EThank%20you%20!!%3C%2FP%3E%3C%2FLINGO-BODY%3E
Occasional Contributor

Hi,

 

is there any way to do this in a oneliner manner ?

 

#Removing records older than 365 days
$tmp = $IPRouteInformation.clone()
foreach($k in $tmp.GetEnumerator()){
    if($k.Value.TimeID -lt (date (get-date).AddDays(-365) -Format yyyyMMddhhmmssfff)){
        $IPRouteInformation.Remove($k.Key)
    }
}

 

 

I tried different things but seems like get-enumerator is blocking me. The code bellow does nothing.

 

$IPRouteInformation.count
$IPRouteInformation.GetEnumerator() | ? { $_.Value.TimeID -lt (date (get-date).AddDays(-1) -Format yyyyMMddhhmmssfff) } | % {$IPRouteInformation.Remove($_)}
$IPRouteInformation.count

 

 

Thank you for your help.

2 Replies

@John_Dodo 

 

Hashtables aren't great for this kind of thing. It'd be easier and far more efficient with a list than a dictionary, but I'll still speak to the request above.

 

As you've already noted, one option is to call .Clone() and then look to use .Remove() to remove items you don't. The problem with this approach is it doesn't scale well since you have to perform a full duplication of the original list's references (i.e. a shallow copy.) If the original list is very large, this can prove quite inefficient.

 

Conversely, if you expect that the final list will be roughly the same size as the initial list (i.e. you're not filtering out all that much) then any efficiency gains you might achieve using another approach are offset by how easy using .Clone() makes things.

 

However, if you're expecting the final list to be much smaller (a subjective statement, but let's say less than half), then you'd be better off creating an empty second hashtable and calling the .Add() method on it to add just the records you wish to keep. This approach avoids a full duplication of the original list.

 

Neither approach is as efficient as using a list but it illustrates that there are options even within the hashtable context.

 

# Example 1: Growing a new list (potentially more efficient - depends on size of original collection.)
$FinalList = @{};
$Ticks = [datetime]::Now.AddDays(-365).Ticks;
$IPRouteInformation.GetEnumerator() | ForEach-Object { if ($_.Value.TimeID.Ticks -ge $Ticks) { $FinalList.Add($_); } }

# Example 2: Fully-duplicating the original list then removing entries (i.e. shrinking the new list).
$Ticks = [datetime]::Now.AddDays(-365).Ticks;
$IPRouteInformation.Clone().GetEnumerator() | ForEach-Object { if ($_.Value.TimeID.Ticks -lt $Ticks) { $IPRouteInformation.Remove($_.Key); } }

 

I've assumed from your code that "TimeID" is a [datetime] object. If it's a string, let me know and I'll change my examples to suit if you need me to.

 

Cheers,

Lain

@LainRobertson thank you very much, very useful as usual.

 

I will review these lines but I already know that it's going to work.

 

Finally what I really understand is that I need to learn what a list is (never used it) and how it compares to arrays and hashtables, and consequently, when we do need arrays, as hastables and/or list seem to be much more convenient.

Also I quite often do hash of hashes variables. I wonder if the right answer is not a list of hashes. To be studied...

 

Thank you !!