Add n values together to get x in Powershell - arrays

I have an array of values (about 20 of them), ranging from about 9.5 to 0.001. I need to add the values together to get a number anywhere between 15 and 85 (Or get the nearest value possible)
Test data:
[9.912,7.414,5.512, 5.43,5.303,5.041,5.025,4.944,3.509, 3.3,3.191,3.076,2.914,2.753, 2.29,1.955,1.917,1.822,1.626,1.526,1.489,1.482,1.362,1.354,1.311,1.222,1.154,0.904,0.799, 0.79,0.657,0.643,0.618,0.615,0.592,0.564,0.484, 0.48,0.447,0.419,0.415,0.328,0.325,0.288, 0.26,0.251,0.248,0.23,0.223,0.221,0.154,0.129,0.128,0.126,0.103,0.102,0.099,0.092,0.077,0.071,0.066, 0.06,0.046,0.037,0.028,0.023,0.007,0.003,0.003,0.003,0.002,0.001,0.001,0.001,0.001]
Can anyone help me/is this even possible?

You could do it like that:
$arr = #(9.912,7.414,5.512, 5.43,5.303,5.041,5.025,4.944,3.509, 3.3,3.191,3.076,2.914,2.753, 2.29,1.955,1.917,1.822,1.626,1.526,1.489,1.482,1.362,1.354,1.311,1.222,1.154,0.904,0.799, 0.79,0.657,0.643,0.618,0.615,0.592,0.564,0.484, 0.48,0.447,0.419,0.415,0.328,0.325,0.288, 0.26,0.251,0.248,0.23,0.223,0.221,0.154,0.129,0.128,0.126,0.103,0.102,0.099,0.092,0.077,0.071,0.066, 0.06,0.046,0.037,0.028,0.023,0.007,0.003,0.003,0.003,0.002,0.001,0.001,0.001,0.001)
$output = 0
$arr | foreach {
if(($output + $_) -le 85){
$output += $_
}
}
$output
The above example will return a number close to but less than 85

Related

Powershell: Find number of occurrences of a specific numeric value from an integer array

I have an integer array like below and I wanted to count number of 1's in that array in powershell, Can anyone help me here please,
[array]$inputs = 81,11,101,1811,1981
$count = 0
foreach($input in $inputs)
{
Write-Host "Processing element $input"
$count += ($input -like "*1*" | Measure-Object).Count
}
Write-Host "Number of 1's in the given array is $count"
It gives me only 5 1's in that array but expected answer is 10. Any help would be appreciated
Starting with a side note:
Don't use $Input as a custom variable as it is a preserved automatic variable
For what you are trying:
You iterating trough an array and check whether each item (with will automatically type cast to a string) is -like a 1 preceded by any number of characters and succeeded by any number of characters which is either true or false (and not the total number of ones in the string).
Instead
You might want to use the Select-String cmdlet with the -AllMatches switch which counts all the matches:
[array]$inputs = 81,11,101,1811,1981
$count = 0
foreach($i in $inputs)
{
Write-Host "Processing element $input"
$count += ($i | Select-String 1 -AllMatches).Matches.Count
}
Write-Host "Number of 1's in the given array is $count"
In fact, thanks to the PowerShell member enumeration feature, you do not even have to iterate through each array item for this, and just simplify it to this:
[array]$inputs = 81,11,101,1811,1981
$count = ($Inputs | Select-String 1 -AllMatches).Matches.Count
Write-Host "Number of 1's in the given array is $count"
Number of 1's in the given array is 10
I solved the above issue with below script,
[string]$inputs = 81,11,101,1811,1981
$count = 0
foreach($i in $inputs.ToCharArray())
{
if($i -eq "1")
{$count++}
}
Write-Host "Number of 1's in the given array is $count"

Averaging part of an array powershell

Hi I am trying to return the average for part of an array, when the array is set out like
$multi = New-Object 'object[,]' $nucount,($readings + 2 )
and contains "1,4,2,6,3,4,5,nameofitem, cost of item"
I want to get an average for the first 7 elements which I know will always be the first 7 and always be a number. In the case above the number of elements containing a number will be held in a variable called $readings. So the sum I want to do is add up the elements in the array up to the $readings value. and then device by $readings.
I know of ways like this for the whole array
$Avg = ($array | Measure-Object -Average);
$Avg.Average;
or simple looping through and calculating the average buy adding and deviding.
But is there any short hand way to do this. I was going to used the ... operator
$multi[2,0..$readings] but i get an error of
Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.Int32".
Any help with this would be great if it can provide a short code to replace the
for (x=1 ; $x -eq $readings ; $X++)
{
$sum = $sum + $multi[2,$x]
{
$avg= $sum/$readings
You are going right way:
$readings=7
$array=1,4,2,6,3,4,5,"nameofitem", "cost of item"
$Avg = ($array[0..($readings - 1)] | Measure-Object -Average);
$Avg.Average;
Note that arrays are zero based…
maybe it works like this:
0..$readings | % { $sum += $multi[$_] }
or if not:
0..[int]$readings | % { $sum += $multi[$_] }
and first you have to initate $sum
$sum = 0
0..$readings | % { $sum += $multi[$_] }
$avg = $sum/$readings
here is another eample of what will work:
$arr = 1..50
$readings = 15
$arr[2..$readings]
regards Eldo.Ob
As you have indicated in your comment that array is 1-dimentional indeed, solution is straightforward:
$multi | select -First 7 | Measure-Object -Average
Try this:
$avg = (0..7 |% { $multi[2,$_] } | Measure-Object -Average)

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

How to store outlook email body in array - Powershell?

the script below reads my outlook emails but how do I access the output. I'm new too Powershell and I'm still getting used to certain things. I just want to get the body of 10 unread outlook emails and store them in an Array called $Body.
$olFolderInbox = 6
$outlook = new-object -com outlook.application;
$ns = $outlook.GetNameSpace("MAPI");
$inbox = $ns.GetDefaultFolder($olFolderInbox)
#checks 10 newest messages
$inbox.items | select -first 10 | foreach {
if($_.unread -eq $True) {
$mBody = $_.body
#Splits the line before any previous replies are loaded
$mBodySplit = $mBody -split "From:"
#Assigns only the first message in the chain
$mBodyLeft = $mbodySplit[0]
#build a string using the –f operator
$q = "From: " + $_.SenderName + ("`n") + " Message: " + $mBodyLeft
#create the COM object and invoke the Speak() method
(New-Object -ComObject SAPI.SPVoice).Speak($q) | Out-Null
}
}
This may not be a factor here, since you're looping through only ten elements, but using += to add elements to an array is very slow.
Another approach would be to output each element within the loop, and assign the results of the loop to $body. Here's a simplified example, assuming that you want $_.body:
$body = $inbox.items | select -first 10 | foreach {
if($_.unread -eq $True) {
$_.body
}
}
This works because anything that is output during the loop will be assigned to $body. And it can be much faster than using +=. You can verify this for yourself. Compare the two methods of creating an array with 10,000 elements:
Measure-Command {
$arr = #()
1..10000 | % {
$arr += $_
}
}
On my system, this takes just over 14 seconds.
Measure-Command {
$arr = 1..10000 | % {
$_
}
}
On my system, this takes 0.97 seconds, which makes it over 14 times faster. Again, probably not a factor if you are just looping through 10 items, but something to keep in mind if you ever need to create larger arrays.
define $body = #(); before your loop
Then just use += to add the elements
Here's another way:
$body = $inbox.Items.Restrict('[Unread]=true') | Select-Object -First 10 -ExpandProperty Body

Comparing values between arrays is very slow

I am a novice Powershell user and would like help with the following:
I am comparing the values in one one array with that of another. If they match, I write the value to a cell, if there is no match, the cell is highlighted red. However, with only two small arrays (each ~200 values) the search takes hours. There must be better way, please help.
$ArrFinal = $arrA + $arrB + $arrC + $arrD
$ArrFinal = $ArrFinal | select -uniq | sort-object
for ($k=1; $k -lt $ArrFinal.length; $k++)
{
for ($j=1; $j -lt $arrA.length; $j++)
{
if($ArrFinal[$k] -like $arrA[$j])
{
$cells.item($k+1,2)=$arrA[$j]
$cells.item($k+1,2).Interior.ColorIndex = 2
break
}
else
{
$cells.item($k+1,2).Interior.ColorIndex = 3
}
}
}
Assuming you're talking about Excel here: don't color each cell separately. Set ColorIndex to 3 once for the entire range and only change a cell's color when you actually change its value. Better yet, use a conditional format that will color empty cells differently from non-empty cells.
Also I'd drop the inner loop. You want to check if the 2nd array contains the value from the 1st one, so you can just use the -contains operator and write the value from the 1st array to the cell ($ArrFinal[$k] and $arrA[$j] are equal after all).
$ArrFinal = $arrA + $arrB + $arrC + $arrD | select -uniq | sort-object
for ($k=1; $k -lt $ArrFinal.length; $k++) {
if ($arrA -contains $ArrFinal[$k]) {
$cells.Item($k+1, 2) = $ArrFinal[$k]
$cells.Item($k+1, 2).Interior.ColorIndex = 2
}
}

Resources