Need to the limit the results of this to the first 20.
Is there another way I can loop this to get 20 results at a time?
$Array = #()
#Fill $Array with Data
$List = $Array | ForEach-Object {"$_`n"}
Personally, I would limit ther returned results prior to the looping construct. My example uses the names of service objects, but I needed something to use...
$TOlist = (Get-Service).Name
$TOlist | Select-Object -First 20 | ForEach-Object {
$_
}
You'll need to use at least two lines here. As far as I'm aware, you can't both assign your $TOlist variable and start sending each value down the pipeline.
Related
I have two array's, one contains multiple columns from a CSV file read in, and the other just contains server names, both type string. For this comparison, I plan on only using the name column from the CSV file. I don't want to use -compare because I want to still be able to use all CSV columns with the results. Here is an example of data from each array.
csvFile.Name:
linu40944
windo2094
windo4556
compareFile:
linu40944
windo2094
linu24455
As you can see, they contain similar server names, except $csvFile.Name contains 25,000+ records, and $compareFile contains only 3,500.
I've tried:
foreach ($server in $compareFile) {
if ($csvFile.Name -like $server) {
$count++
}
}
Every time I run this, it takes forever to run, and results in $count having a value in the millions when it should be roughly 3,000. I've tried different variations of -match, -eq, etc. where -like is. Also note that my end goal is to do something else where $count is, but for now I'm just trying to make sure it is outputting as much as it should, which it is not.
Am I doing something wrong here? Am I using the wrong formatting?
One possible thought given the size of your data.
Create a hashtable (dictionary) for every name in the first/larger file. Name is the Key. Value is 0 for each.
For each name in your second/smaller/compare file, add 1 to the value in your hashtable IF it exists. If it does not exist, what is your plan???
Afterwards, you can dump all keys and values and see which ones are 0, 1, or >1 which may or may not be of value to you.
If you need help with this code, I may be able to edit my answer. Since you are new, to StackOverflow, perhaps you want to try this first yourself.
Build custom objects from $compareFile (so that you can compare the same property), then use Compare-Object with the parameter -PassThru for the comparison. Discriminate the results using the SideIndicator.
$ref = $compareFile | ForEach-Object {
New-Object -Type PSObject -Property #{
'Name' = $_
}
}
Compare-Object $csvFile $ref -Property Name -PassThru | Where-Object {
$_.SideIndicator -eq '<='
} | Select-Object -Property * -Exclude SideIndicator
The trailing Select-Object removes the additional property SideIndicator that Compare-Object adds to the result.
I am trying to find a way to sort a CSV by two fields and retrieve only the latest item.
CSV fields: time, computer, type, domain.
Item that works is below but is slow due to scale of CSV and I feel like there is a better way.
$sorted = $csv | Group-Object {$_.computer} | ForEach {$_.Group | Sort-Object Time -Descending | Select-Object -First 1}
As Lee_Dailey suggests, you'll probably have better luck with a hashtable instead, Group-Object (unless used with the -NoElement parameter) is fairly slow and memory-hungry.
The fastest way off the top of my head would be something like this:
# use the call operator & instead of ForEach-Object to avoid overhead from pipeline parameter binding
$csv |&{
begin{
# create a hashtable to hold the newest object per computer
$newest = #{}
}
process{
# test if the object in the pipeline is newer that the one we have
if(-not $newest.ContainsKey($_.Computer) -or $newest[$_.Computer].Time -lt $_.Time){
# update our hashtable with the newest object
$newest[$_.Computer] = $_
}
}
end{
# return the newest-per-computer object
$newest.Values
}
}
I have script which retrieves data from a web service in XML format. Certain elements can be there - or not - and can contain one or more sub elements. Therfore I retrieve the value with this:
$uid = $changeRecord.newAttrs | Where{$_.name -eq 'uid'} | Select -ExpandProperty Values | select -Index 0
This works fine. Mostly there is only one sub element in the <values> part of the answer and even if not, I am only interested in the first one. However, the last part | select -Index 0 produces silent warnings into the Windows Event Log (see also here ) if there is only one element within <values>. Therefore I would like to get rid of the error.
So I am looking for a way to achieve the same behaviour without it throwings errors - and possible not just put try-catch around.
Thanks!
// Update: As discussed below, the answers presented so far do not solve the issue. The closest by now is
([array]($changeRecord.newAttrs | Where{$_.name -eq 'uid'} | Select -ExpandProperty Values))[0]
This, however, fails with an error if the array does not contain any elements. Any idea if this can be handled as well within one line?
Have you tried this: Select-Object -First 1 ?
$uid = $changeRecord.newAttrs |
Where-Object {$_.name -eq 'uid'} |
Select-Object -ExpandProperty Values |
Select-Object -First 1
Select -Index n is only meant to be used on arrays as it will explicitly select from that index in the array. Therefore you will have issues when doing it on a single object. Select -First n will get you n number of objects off the pipeline.
All that said, when I am calling a command and the results may either be a single item or an array of items, I generally declare the variable as an array or cast the value as an array and then even if I get a single object back from the command it will be stored in an array. That way no matter what gets returned, I am treating it the same way. So in your case:
$uid = [array]($changeRecord.newAttrs | Where{$_.name -eq 'uid'} | Select -ExpandProperty Values) | select -Index 0
So, I finally found a solution which is probably fine for me:
$valueArray = [array]($changeRecord.newAttrs | Where{$_.name -eq 'uid'} | Select -ExpandProperty Values)
if(($valueArray -ne $null) -and ($valueArray.Count -gt 0))
{
$value = $valueArray.GetValue(0)
}
else
{
$value = "null..."
}
I put the whole thing into an array first and then check if the array contains any elements. Only if so, I get the first value.
Thanks for everybodys help!
I want to return an object from an array who's property has the highest value. Currently I am doing the following
Get-VM | Sort-Object -Property ProvisionedSpaceGB | Select-Object -Last 1
This works but is inefficient. I don't need the entire array sorted, I just need the object with largest value. Ideally I would use something like
Get-VM | Measure-Object -Property ProvisionedSpaceGB -Maximum
but this only returns the value of the object property, not the entire object. Is there a way to have measure-object return the base object?
Not directly. Measure-Object is intended to be an easy way to grab such values, not their input objects. You could get the maximum from Measure-Object and then compare against the array, but it takes a few steps:
$array = Get-VM
$max = ($array | measure-object -Property ProvisionedSpaceGB -maximum).maximum
$array | ? { $_.ProvisionedSpaceGB -eq $max}
You could also forgo Measure-Object entirely and iterate through the set, replacing the maximum and output as you go.
$max = 0
$array | Foreach-Object
{
if($max -le $_.ProvisionedSpaceGB)
{
$output = $_
$max = $_.ProvisionedSpaceGB
}
}
$output
This is a little dirtier so as to always return a single value. It would need a minor adjustment if you were to reuse it in a case where there may be multiple values that have the same maximum (filesize lengths when using Get-ChildItem, for example). It will replace $output with the latter iterate in a case where two or more objects have the same value for ProvisionedSpaceGB. You could turn $output into a collection easily enough to fix that.
I prefer the former solution myself, but I wanted to offer a different way to think about the problem.
You can use this:
$array = Get-VM | Sort-Object -Property ProvisionedSpaceGB -Descending
$array[0]
I am trying to seperate values in an array so i can pass them to another function.
Am using the select-Object function within a for loop to go through each line and separate the timestamp and value fields.
However, it doesn't matter what i do the below code only displays the first select-object variable for each line. The second select-object command doesn't seem to work as my output is a blank line for each of the 6 rows.
Any ideas on how to get both values
$ReportData = $SystemStats.get_performance_graph_csv_statistics( (,$Query) )
### Allocate a new encoder and turn the byte array into a string
$ASCII = New-Object -TypeName System.Text.ASCIIEncoding
$csvdata = $ASCII.GetString($ReportData[0].statistic_data)
$csv2 = convertFrom-CSV $csvdata
$newarray = $csv2 | Where-Object {$_.utilization -ne "0.0000000000e+00" -and $_.utilization -ne "nan" }
for ( $n = 0; $n -lt $newarray.Length; $n++)
{
$nTime = $newarray[$n]
$nUtil = $newarray[$n]
$util = $nUtil | select-object Utilization
$util
$tstamp = $nTime | select-object timestamp
$tstamp
}
Let me slightly modify the processing code, if it will help.
$csv2 |
Where-Object {$_.utilization -ne "0.0000000000e+00" -and $_.utilization -ne "nan" } |
Select-Object Utilization,TimeStamp
It will produce somewhat different output, but that should be better for working with.
The result are objects with properties Utilization and TimeStamp. You can pass them to the another function as you mention.
Generally it is better to use pipes instead of for loops. You don't need to care about indexes and it works with arrays as well as with scalar values.
If my updated code won't work: is the TimeStamp property really filled with any value?