why select -index array doesn't work, powershell - arrays

i need to add numbers that don't belong to a linear sequence to retrieve specified indexed folders, i make this attempts :
$a =ls | ?{$_.psiscontainer} | sort creationtime
Then i try to select only some of the folders(fail):
$a | select -Index (100, 101, (103..109))
Whit this simplified array i don't have problems:
$a | select -Index (103..109)
how can i add those numbers?
I try this, forcing the conversion to an array, but the process of conversion fails and i don't know why, if i get the type of the inner members of the array they are already Int32, so i don't understand the error.
$a | select -Index #(100, 101, (103..109))

Parameter -Index is of type int[]. That means that what you pass in as an argument, it has to be an array of numbers.
Imho the simplest way is just to add the arrays like this:
0..100 | select -Index (2,3,5 + 20..30 + 50,60)
Note that you don't have to do ((2,3,5) + (20..30) + (50,60)), because the comma operator has higher priority than plus and interval operator.

For this example, you can get the same result using array slicing:
$a[100,101 + 103..109]

Related

Powershell | Array - Select from - to

is there a way to select some objects of an array "from-to"?
$data = #('A1','A2','A3','A4','B1','B2','B3','B4')
$data |Select-Object A3 - B2
in my example i want to select only A3-B2 but i dont want to write them down like this
$data |Select-Object A3, A4, B1, B2
Since you are operating on array, you can directly slice the array from 2 to 5. ie,
$data[2..5]
If you still want to use Select-Object, you can use it with -Index parameter. This will Selects objects from an array based on their index values.
$data | Select-Object -Index #(2..5)
You can retrieve part of the array using a range operator for the index in your case it will be from the 3rd item to the 6th
$data [2..5]
or you can do this with negative numbers counts
$data [-3..-6]
you can read more on the documentation

Purpose of # in Looping Arrays?

I'm currently learning PowerShell, starting with the basics, and I've gotten to arrays. More specifically, looping arrays. I noticed that when declaring an array by itself, it's just simply written as
$myarray = 1, 2, 3, 4, 5
However, when an array is being declared with the intention of it being looped, it is written as
$myarray = #(1, 2, 3, 4, 5)
Out of curiosity, I tried running the code for looping through the array both with and without the # sign just to see if it would work and it was displayed within the string I created in the exact same way for both.
My question is what is the purpose of the # sign? I tried looking it up, but couldn't find any results.
It's an alternative syntax for declaring static arrays, but there are some key details to understanding the differences in syntax between them.
#() is the array sub-expression operator. This works similarly to the group-expression operator () or sub-expression operator $() but forces whatever is returned to be an array, even if only 0 or 1 element is returned. This can be used inline wherever an array or collection type is expected. For more information on these operators, read up on the Special Operators in the documentation for PowerShell.
The 1, 2, 3, 4 is the list-expression syntax, and can be used anywhere an array is expected. It is functionally equivalent to #(1, 2, 3, 4) but with some differences in behavior than the array subexpression operator.
#( Invoke-SomeCmdletOrExpression ) will force the returned value to be an array, even if the expression returns only 0 or 1 element.
# Array sub-expression
$myArray = #( Get-Process msedge )
Note this doesn't have to be a single cmdlet call, it can be any expression, utilizing the pipeline how you see fit. For example:
# We have an array of fruit
$fruit = 'apple', 'banana', 'apricot', 'cherry', 'a tomato ;)'
# Fruit starting with A
$fruitStartingWithA = #( $fruit | Where-Object { $_ -match '^a' } )
$fruitStartingWithA should return the following:
apple
apricot
a tomato ;)
There's another way to force an array type and I see it often mentioned on Stack Overflow as a cool trick (which it is), but with little context around its behavior.
You can use a quirk of the list-expression syntax to force an array type, but there are two key differences between this and using the array sub-expression operator. Prefixing an expression or variable with a comma , will force an array to be returned, but the behavior changes between the two. Consider the following example:
# Essentially the same as #( $someVar )
$myArray1 = , $someVar
# This behaves differently, read below
$myArray2 = , ( Invoke-SomeCmdletOrExpression )
#() or prefixing a variable with , will flatten (another word often used here is unroll) the resulting elements into a single array. But with an expression you have to use the group-expression operator if you use the comma-prefix trick. Due to how the grouped expression is interpreted you will end up with an array consisting of one element.
It will not flatten any resulting elements in this case.
Consider the Get-Process example above. If you have three msedge processes running, $myArray.Count will show a count of 3, and you can access the individual processes using the array-index accessor $myArray[$i]. But if you do the same with $myArray2 in the second list-expression example above, $myArray2.Count will return a count of 1. This is essentially now a multi-dimensional array with a single element. To get the individual processes, you would now need to do $myArray2[0].Count to get the process count, and use the array-index accessor twice to get an individual process:
$myArray2 = , ( Get-Process msedge )
$myArray2.Count # ======> 1
# I have 32 Edge processes right now
$myArray2[0].Count # ===> 32
# Get only the first msedge process
$myArray[0][0] # =======> Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
# =======> ------- ------ ----- ----- ------ -- -- -----------
# =======> 430 19 101216 138968 74.52 3500 1 msedge
This can be unclear at first because printing $myArray2 to the output stream will show the same output result as $myArray from the first example, and $myArray1 in the second example.
In short, you want to avoid using the comma-prefix trick when you want to use an expression, and instead use the array sub-expression #() operator as this is what it is intended for.
Note: There will be times when you want to define a static array of arrays but you will be using list-expression syntax anyways, so the comma-prefix becomes redundant. The only counterpoint here is if you want to create an array with an array in the first element to add more arrays to it later, but you should be using a generic List[T] or an ArrayList instead of relying on one of the concatenation operators to expand an existing array (+ or += are almost always bad ideas on non-numeric types).
Here is some more information about arrays in PowerShell, as well as the Arrays specification for PowerShell itself.
The operator you're looking for documentation on consists not only of the # but the ( and ) too - together they make up #(), also known as the array subexpression operator.
It ensures that the output from whatever pipeline or expression you wrap in it will be an array.
To understand why this is useful, we need to understand that PowerShell tends to flatten arrays! Let's explore this concept with a simple test function:
function Test-PowerShellArray {
param(
$Count = 2
)
while($count--){
Get-Random
}
}
This function is going to output a number of random numbers - $Count numbers, to be exact:
PS ~> Test-PowerShellArray -Count 5
652133605
1739917433
1209198865
367214514
1018847444
Let's see what type of output we get when we ask for 5 numbers:
PS ~> $numbers = Test-PowerShellArray -Count 5
PS ~> $numbers.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
Alright, so the resulting output that we've stored in $numbers is of type [Object[]] - this means we have an array which fits objects of type Object (any type in .NET's type system ultimately inherit from Object, so it really just means we have an array "of stuff", it could contain anything).
We can try again with a different count and get the same result:
PS ~> $numbers = Test-PowerShellArray -Count 100
PS ~> $numbers.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
So far so good - we collected multiple output values from a function and ended up with an array, all is as expected.
But what happens when we only output 1 number from the function:
PS ~> $numbers = Test-PowerShellArray -Count 1
PS ~> $numbers.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int32 System.ValueType
Say what? Now we're getting System.Int32 - which is the type of the individual integer values - PowerShell noticed that we only received 1 output value and went "Only 1? I'm not gonna wrap this in an array, you can have it as-is"
For this reason exactly, you might want to wrap output that you intend to loop over (or in other ways use that requires it to be an array):
PS ~> $numbers = Test-PowerShellArray -Count 1
PS ~> $numbers.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int32 System.ValueType
PS ~> $numbers = #(Test-PowerShellArray -Count 1) # #(...) saves the day
PS ~> $numbers.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array

Create and split an array twice all inline in Powershell

I have the following code which works but I am looking for a way to do this all inline without the need for creating the unnecessary variables $myArray1 and $myArray2:
$line = "20190208 10:05:00,Source,Severity,deadlock victim=process0a123b4";
$myArray1 = $line.split(",");
$myArray2 = $myArray1[3].split("=");
$requiredValue = $myArray2[1];
So I have a string $line which I want to:
split by commas into an array.
take the fourth item [3] of the new array
split this by the equals sign into another array
take the second item of this array [1]
and store the string value in a variable.
I have tried using Select -index but I haven't been able to then pipe the result and split it again.
The following works:
$line.split(",") | Select -index 3
However, the following results in an error:
$line.split(",") | Select -index 3 | $_.split("=") | Select -index 1
Error message: Expressions are only allowed as the first element of a pipeline.
$line.Split(',')[3].Split('=')[1]
Try below code:
$requiredValue = "20190208 10:05:00,Source,Severity,deadlock victim=process0a123b4" -split "," -split "=" | select -Last 1
Mudit already provided an answer, here's another about your particular case.
Piping to foreach and accessing 2nd element does the trick:
$line.split(",") | Select -index 3 | % {$_.split("=")[1]}
process0a123b4
That being said, aim for readability and ease of maintenance. There's nothing wrong with having intermediate variables. Memory is cheap nowadays, programmers' time is not. Optimization is due when it's needed and only then after careful profiling to see what's the actual bottleneck.
You could pipe the second split to a foreach
$line.split(",") | Select -index 3 | foreach { $_.split("=") | Select -index 1 }

Convert results to an array

I've got this code:
$myarray = #(#(0..3) | foreach {(#(0..$_) | Measure-Object -Sum).Sum})
[array]::indexof($myarray,2)
$myarray contains the values 0,1,3,6 so I was hoping [array]::indexof($myarray,3) would return the value 2, but it does not. it returns -1.
I'm assuming that the thing I think is an array isn't actually an array, so I'm assuming the thing I'm assigning to $myarray needs to get converted to an array somehow, clearly I'm doing it wrong.
An easier way of getting values in an array by index number is like this:
$myarray[2]
Sorry, misunderstood your problem. To get the first occurrence of a value in the array, try this:
$myarray.IndexOf([double]3)
Figured it out, sorry. Its an array of doubles, not a array of ints. This works (and is actually what I wanted, i.e. the element contiaing 2016):
$array = #(0..64) | foreach {[int](#(0..$_) | Measure-Object -Sum).Sum}
[array]::indexof($array,2016)

Unique Combos from powershell array - No duplicate combos

I'm trying to figure out the best way to get unique combinations from a powershell array. For instance, my array might be
#(B,C,D,E)
I would be hoping for an output like this :
B
C
D
E
B,C
B,D
B,E
C,D
C,E
D,E
B,C,D
C,D,E
B,C,D,E
I do not want re-arranged combos. If combo C,D exists already then I do not want combo D,C. It's redundant for my purposes.
I looked into the functions here : Get all combinations of an array
But they aren't what I want. I've been working on figuring this out myself, but have spent quite a bit of time without success. I thought I'd ask the question here so that if someone else already know I'm not wasting my time.
Thanks!
This is an adaptation from a solution for a C# class I took that asked this same question. For any set find all subsets, including the empty set.
function Get-Subsets ($a){
#uncomment following to ensure only unique inputs are parsed
#e.g. 'B','C','D','E','E' would become 'B','C','D','E'
#$a = $a | Select-Object -Unique
#create an array to store output
$l = #()
#for any set of length n the maximum number of subsets is 2^n
for ($i = 0; $i -lt [Math]::Pow(2,$a.Length); $i++)
{
#temporary array to hold output
[string[]]$out = New-Object string[] $a.length
#iterate through each element
for ($j = 0; $j -lt $a.Length; $j++)
{
#start at the end of the array take elements, work your way towards the front
if (($i -band (1 -shl ($a.Length - $j - 1))) -ne 0)
{
#store the subset in a temp array
$out[$j] = $a[$j]
}
}
#stick subset into an array
$l += -join $out
}
#group the subsets by length, iterate through them and sort
$l | Group-Object -Property Length | %{$_.Group | sort}
}
Use like so:
PS C:>Get-Subsets #('b','c','d','e')
b
c
d
e
bc
bd
be
cd
ce
de
bcd
bce
bde
cde
bcde
Note that computational costs go up exponentially with the length of the input array.
Elements SecondstoComplete
15 46.3488228
14 13.4836299
13 3.6316713
12 1.2542701
11 0.4472637
10 0.1942997
9 0.0867832
My tired attempt at this. I did manage to get it to produce the expected results but how it does it is not as elegant. Uses a recursive functionality.
Function Get-Permutations{
Param(
$theInput
)
$theInput | ForEach-Object{
$element = $_
$sansElement = ($theInput | Where-Object{$_ -ne $element})
If($sansElement.Count -gt 1){
# Build a collection of permutations using the remaining elements that were not isolated in this pass.
# Use the single element since it is a valid permutation
$perms = ,$element
For($elementIndex = 0;$elementIndex -le ($sansElement.Count - 1);$elementIndex++){
$perms += ,#(,$element + $sansElement[0..$elementIndex] | sort-object)
}
# For loop does not send to output properly so that is the purpose of collecting the results of this pass in $perms
$perms
# If there are more than 2 elements in $sansElement then we need to be sure they are accounted for
If($sansElement -gt 2){Get-Permutations $sansElement}
}
}
}
Get-Permutations B,C,D,E | %{$_ -join ","} | Sort-Object -Unique
I hope I can explain myself clearly....So each pass of the function will take an array. Each individual element of that array will be isolated from the rest of the array which is represented by the variables $element and $sansElement.
Using those variables we build individual and progressively larger arrays composing of those elements. Let this example show using the array 1,2,3,4
1
1,2
1,2,3
1,2,3,4
The above is done for each "number"
2
2,1
2,1,3
2,1,3,4
and so forth. If the returned array contains more that two elements (1,2 would be the same as 2,1 in your example so we don't care about pairs beyond one match) we would take that array and run it through the same function.
The real issue is that the logic here (I know this might be hard to swallow) creates several duplicates. I suppose you could create a hashtable instead which I will explore but it does not remove the logic flaw.
Regardless of me beating myself up as long as you don't have thousands of elements the process would still produce results.
Get-Permutations would return and array of arrays. PowerShell would display that one element per line. You asked for comma delimited output which is where -join comes in. Sort-Object -Unique takes those sorted string an discards the duplicates.
Sample Output
B
B,C
B,C,D
B,C,D,E
B,C,E #< Missing from your example output.
B,D
B,D,E #< Missing from your example output.
B,E
C
C,D
C,D,E
C,E
D
E

Resources