Create and split an array twice all inline in Powershell - arrays

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 }

Related

PowerShell - Create an array that ignores duplicate values

Curious if there a construct in PowerShell that does this?
I know you can do this:
$arr = #(1,1,1,1,2,2,2,2,3,3,3,3,3,4,4,4,4,4)
$arr = $arr | Get-Unique
But seems like performance-wise it would be better to ignore the value as you are entering it into the array instead of filtering out after the fact.
If are you inserting a large number of items in to an array (thousands) the performance does drop, because the array needs to be reinitialized every time you add to it so it may be better in your case, performance wise, to use something else.
Dictionary, or HashTable could be a way. Your single dimensional unique array could be retrieved with $hash.Keys For example:
$hash = ${}
$hash.Set_Item(1,1)
$hash.Set_Item(2,1)
$hash.Set_Item(1,1)
$hash.Keys
1
2
If you use Set_Item, the key will be created or updated but never duplicated. Put anything else for the value if you're not using it, But maybe you'll have a need for a value with your problem too.
You could also use an Arraylist:
Measure-Command -Expression {
$bigarray = $null
$bigarray = [System.Collections.ArrayList]#()
$bigarray = (1,1,1,1,2,2,2,2,3,3,3,3,3,4,4,4,4,4)
$bigarray | select -Unique
}
Time passed:
TotalSeconds : 0,0006581
TotalMilliseconds : 0,6581
Measure-Command -Expression {
$array = #(1,1,1,1,2,2,2,2,3,3,3,3,3,4,4,4,4,4)
$array | select -Unique
}
Time passed:
TotalSeconds : 0,0009261
TotalMilliseconds : 0,9261

#powershell storing and retrieving values from/to array or a hash table

I wondering if there's a way to store/retrieve data in a hash table or an array. For instance,
$a = get-process
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id SI ProcessName
------- ------ ----- ----- ----- ------ -- -- -----------
156 9 1924 220 ...23 0.06 3788 4 ActionUriServer
364 19 10676 22196 ...01 0.75 11432 4 ApplicationFrameHost
155 9 6360 10388 ...98 5.47 14940 0 audiodg
I need this to be stored in a variable. When I want this to be retried, I should be able to use the index value.
for e.g. $a[1][1] should fetch me the value 'Handles'
I roughly have an idea about multi dimensional arrays, but then, not really sure how to push these values to the variable and have it indexed.
will foreach workout ?
If you really want to parse the formatted output rather than operate on the objects returned by Get-Process (or customize formatting using one of the Format-* cmdlets), you can try this:
Get-Process | Out-String -Stream | % { $a = #() } { $a += $null; $a[-1] = -split $_ }
Out-String -Stream is required to obtain the formatted-for-output string representations of the objects returned by Get-Process, and -Stream passes each resulting line individually through the pipeline (by default, Out-String passes the entire resulting string at once).
The above is simple, but not efficient, because the array is recreated for each input line; to remedy that, you could use a [System.Collections.Generic.List[string[]]] instance instead.
PowerShell arrays are 0-based, so it'll be $a[1][0] that contains Handles
(The reason that it is row 1 is that the formatted output starts with a blank line, but that could easily be fixed.)

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

Create object from array of keys and values

I've been banging my head against the wall for several hours on this and just can't seem to find a way to do this. I have an array of keys and an array of values, how can I generate an object? Input:
[["key1", "key2"], ["val1", "val2"]]
Output:
{"key1": "val1", "key2": "val2"}
Resolved this on github:
.[0] as $keys |
.[1] as $values |
reduce range(0; $keys|length) as $i ( {}; . + { ($keys[$i]): $values[$i] })
The current version of jq has a transpose filter that can be used to pair up the keys and values. You could use it to build out the result object rather easily.
transpose | reduce .[] as $pair ({}; .[$pair[0]] = $pair[1])
Just to be clear:
(0) Abdullah Jibaly's solution is simple, direct, efficient and generic, and should work in all versions of jq;
(1) transpose/0 is a builtin in jq 1.5 and has been available in pre-releases since Oct 2014;
(2) using transpose/0 (or zip/0 as defined above), an even shorter but still simple, fast, and generic solution to the problem is:
transpose | map( {(.[0]): .[1]} ) | add
Example:
$ jq 'transpose | map( {(.[0]): .[1]} ) | add'
Input:
[["k1","k2","k3"], [1,2,3] ]
Output:
{
"k1": 1,
"k2": 2,
"k3": 3
}
Scratch this, it doesn't actually work for any array greater than size 2.
[map(.[0]) , map(.[1])] | map({(.[0]):.[1]}) | add
Welp, I thought this would be easy, having a little prolog experience... oh man. I ended up banging my head against a wall too. Don't think I'll ever use jq ever again.
Here is a solution which uses reduce with a state object holding an iteration index and a result object. It iterates over the keys in .[0] setting corresponding values in the result from .[1]
.[1] as $v
| reduce .[0][] as $k (
{idx:0, result:{}}; .result[$k] = $v[.idx] | .idx += 1
)
| .result

why select -index array doesn't work, powershell

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]

Resources