How to get range between 0 and a number with Powershell? - powershell

From a number (163 - 1 ) and a scope (20), I try to get ranges (0..19, 20..39, ..., 160..162)
I can get all exept 160..162
$Counter = 163 -1
$Scope = "20"
$Modulo = $Counter % $Scope
$NbrLoop = [Math]::Ceiling($Counter / $Scope)
$j = 0
For($i=0; $i -le $Counter;$i++)
{
If($i % $Scope -eq 0 -and $i -ne 0)
{
$k = $i
$j..--$k
$j = $i
""
}
}
Could you please let me know how to proceed to get last range (160,161,162) ?

modify condition in if to accept last iteration
If($i % $Scope -eq 0 -and $i -ne 0 -or $i -eq $Counter)
however --$k must not be done int that case
$k = If ( $i -eq $Counter ) { $i } else { $i-1 }
$j..$k
Otherwise the number of iterations can be reduced
For($i=0; $i -lt $NbrLoop;$i++)
{
$j = $i * $scope;
$k = If( $i -eq $NbrLoop -1 ) { $Counter } else { ($i+1)*$scope -1 };
$j..$k
""
}

Related

How to prevent random output?

When I run my script it adds an output I haven't requested.
$input = Read-Host -p "Message?";
$morseFile = Get-Content C:\Users\lukas.downes\Documents\Script\morse.csv
$letter;
$code;
$outputAsString = "";
for ($j = 0; $j -lt $input.Length; $j++)
{
for ($i = 1; $i -lt $morseFile.Length; $i++)
{
$letter = ([string]$morseFile[$i]).Split(',')[0];
$code = ([string]$morseFile[$i]).Split(',')[1];
if ($input[$j] -eq $letter)
{
$outputAsString += $code;
write-host ("" + $letter + ": " + $code)
}
}
}
Write-Host $outputAsString
and this outputs:
> Message?: sos
> Z
> --..
> S: ...
> O: ---
> S: ...
> ...---...
which is rather weird, as I don't even use a "Z" in my script.
If your input csv morse.csv looks anything like this:
Letter,Code
O,---
P,.--.
Q,--.-
R,.-.
S,...
Then this should work for you:
# import the csv as array of objects
$codes = Import-Csv -Path 'C:\Users\lukas.downes\Documents\Script\morse.csv'
# for more speed, change this array of objects into a lookup Hashtable
$morse = #{}
foreach ($item in $codes) {
$morse[$item.Letter] = $item.Code
}
$inputString = Read-Host -p "Message?"
if (![string]::IsNullOrWhiteSpace($inputString)) {
$inputString.ToUpper().ToCharArray() | ForEach-Object {
'{0}: {1}' -f $_, $morse[$_.ToString()] # or: $morse["$_"]
}
}
When entering 'sos', the output is:
S: ...
O: ---
S: ...
P.S. Don't use $input as self-defined variable, because it is an Automatic variable

Why Write-Verbose messages don't appear from conditions

I'm trying to receive Verbose messages of every sorting step and run my script with -Verbose key, but I don't receive any verbose message from the conditionals (loops). I receive my messages only if I call them outside the conditionals. Please help
[CmdletBinding()]
param()
set-location "\test folder"
$listOfItems = Get-ChildItem | Select-Object -Property Name, Mode, LastWriteTime, #{label = "FileSize (MB)";expression = {$_.Length/1024/1024}}
$bufferItem = 0
for ($i = $listOfItems.Length - 1; $i -eq 0; $i--) {
for ($j = 0; $i -lt $i; $j++) {
if ($listOfItems[$j]."FileSize (MB)" -gt $listOfItems[$j + 1]."FileSize (MB)") {
Write-Verbose -Message "Transfer the value of the buffer variable to the element below the list"
continue
}
elseif ($listOfItems[$j]."FileSize (MB)" -lt $listOfItems[$j + 1]."FileSize (MB)") {
$bufferItem = $listOfItems[$j]
$listOfItems[$j] = $listOfItems[$j + 1]
$listOfItems[$j + 1] = $bufferItem
}
Write-Verbose -Message "Transfer the value of the buffer variable to the element below the list"
}
}
There's a bug in your inner loop condition:
for ($j = 0; $i -lt $i; $j++) {
Since $i -lt $i will never evaluate to $true, the inner loop body won't ever execute, and no Write-Verbose statement is ever reached.
Fix the loop condition and you'll find Write-Verbose works perfectly fine in conditional statements.

Store return value to variable

I have the below code to convert assign GB/TB value based on size
$datastoreCapacity = $store.CapacityGB
$postfixes = #("GB", "TB", "PB" )
for ($i=0; $datastoreCapacity -ge 1024 -and $i -lt $postfixes.Length; $i++) { $datastoreCapacity = $datastoreCapacity / 1024; }
return "" + [System.Math]::Round($datastoreCapacity,2) + " " + $postfixes[$i];
$datastoreFree = $store.FreeSpaceGB
$postfixes = #("GB", "TB", "PB" )
for ($i=0; $datastoreFree -ge 1024 -and $i -lt $postfixes.Length; $i++) { $datastoreFree = $datastoreFree / 1024; }
return "" + [System.Math]::Round($datastoreFree,2) + " " + $postfixes[$i];
But when I am trying to assign the return value to variable like below i am getting error
$datastoreCapacity = return "" + [System.Math]::Round($datastoreCapacity,2) + " " +
Please let me know how can i store the value in variable
Why not make a small helper utility function for this:
function Format-Capacity ([double]$SizeInGB) {
$units = 'GB', 'TB', 'PB'
for ($i = 0; $SizeInGB -ge 1024 -and $i -lt $units.Count; $i++) {
$SizeInGB /= 1024
}
'{0:N2} {1}' -f $SizeInGB, $units[$i]
}
Then getting the formatted sized is as easy as:
$datastoreCapacity = Format-Capacity $store.CapacityGB
$datastoreFree = Format-Capacity $store.FreeSpaceGB

How to get the string name which has failed consecutively for 3 days in powershell

I have data in a text file with the following columns.
Date
StringName
1/1/2021
ABC
2/1/2021
ABC
3/1/2021
ABC
1/1/2021
XYZ
2/1/2021
XYZ
3/1/2021
ABC
4/1/2021
ZZZ
I want only the StringName in the output which has consecutively failed for 3 days or more & ignore the rest. In the above data, ABC has failed for 1 2 3 days consecutively in Calendar month January. So the output should be ABC. If there are more than one stringName then the output should be like below
StringName
ABC
XYZ
Finally, I was able to achieve this. Thank you all
$file_data = get-content "c:\temp\txt.txt"
$dict_counter = #{}
$dict_dates = #{}
Foreach ($line in $file_data)
{
$values = $line.split(" ")
$pattern = '\d{4}-\d{2}-\d{2}'
[System.Text.RegularExpressions.Regex]$regex = New-Object System.Text.RegularExpressions.Regex($pattern)
[System.Text.RegularExpressions.Match]$m = $regex.Match($line)
if($m.Success)
{
$current_date = $m.Value
$sch_name = $values[7]
if($dict_dates.containsKey($sch_name))
{
$existing_date = $dict_dates.get_item($sch_name)
if($existing_date -ne $current_date)
{
$existing_date_format = [datetime]::parseexact($existing_date, 'yyyy-MM-dd', $null)
$current_date_format = [datetime]::parseexact($current_date, 'yyyy-MM-dd', $null)
$difference = (new-timespan -start $existing_date_format -end $current_date_format).days
if($dict_counter.containsKey($sch_name))
{
$diff = $dict_counter.get_item($sch_name)
$diff_new = [string]$diff + "," + [string]$difference
$dict_counter.set_item($sch_name, $diff_new)
}
else
{
$dict_counter.add($sch_name, $difference)
}
}
}
else
{
$dict_dates.add($sch_name, $current_date)
$dict_counter.add($sch_name, 0)
}
}
}
$fail_count = 0
$flagSequence = $false
$seqcounter = 0
Foreach ($value in $dict_counter.values)
{
if($value -ne 0)
{
$arr1 = $value.split(",")
$arr = foreach($number in $arr1){([int]::parse($number))}
$arr = $arr | sort
For($i = 1; $i -lt $arr.count; $i++)
{
$prev = $arr[$i - 1]
$curr = $arr[$i]
$dff = $curr - $prev
if(($dff -eq 1) -or ($dff -eq -1))
{
$flagSequence = $true
$seqcounter += 1
}
else
{
$flagSequence = $false
$seqcounter = 0
}
}
if($seqcounter -ge 2)
{
$fail_count += 1
}
}
}
$dict_counter
$fail_count

Convert Array of Numbers into a String of Ranges

I was asking myself how easily you could convert an Array of Numbers Like = 1,2,3,6,7,8,9,12,13,15 into 1 String that "Minimizes" the numbers, so Like = "1-3,6-9,12-13,15".
I am probably overthinking it because right now I don't know how I could achieve this easily.
My Attempt:
$newArray = ""
$array = 1,2,3,6,7,8,9,12,13,15
$before
Foreach($num in $array){
If(($num-1) -eq $before){
# Here Im probably overthinking it because I don't know how I should continue
}else{
$before = $num
$newArray += $num
}
}
This should working, Code is self explaining, hopefully:
$array = #( 1,2,3,6,7,8,9,12,13,15 )
$result = "$($array[0])"
$last = $array[0]
for( $i = 1; $i -lt $array.Length; $i++ ) {
$current = $array[$i]
if( $current -eq $last + 1 ) {
if( !$result.EndsWith('-') ) {
$result += '-'
}
}
elseif( $result.EndsWith('-') ) {
$result += "$last,$current"
}
else {
$result += ",$current"
}
$last = $current
}
if( $result.EndsWith('-') ) {
$result += "$last"
}
$result = $result.Trim(',')
$result = '"' + $result.Replace(',', '","') +'"'
$result
I have a slightly different approach, but was a little too slow to answer. Here it is:
$newArray = ""
$array = 1,2,3,6,7,8,9,12,13,15
$i = 0
while($i -lt $array.Length)
{
$first = $array[$i]
$last = $array[$i]
# while the next number is the successor increment last
while ($array[$i]+1 -eq $array[$i+1] -and ($i -lt $array.Length))
{
$last = $array[++$i]
}
# if only one in the interval, output that
if ($first -eq $last)
{
$newArray += $first
}
else
{
# else output first and last
$newArray += "$first-$last"
}
# don't add the final comma
if ($i -ne $array.Length-1)
{
$newArray += ","
}
$i++
}
$newArray
Here is another approach to the problem. Firstly, you can group elements by index into a hashtable, using index - element as the key. Secondly, you need to sort the dictionary by key then collect the range strings split by "-" in an array. Finally, you can simply join this array by "," and output the result.
$array = 1, 2, 3, 6, 7, 8, 9, 12, 13, 15
$ranges = #{ }
for ($i = 0; $i -lt $array.Length; $i++) {
$key = $i - $array[$i]
if (-not ($ranges.ContainsKey($key))) {
$ranges[$key] = #()
}
$ranges[$key] += $array[$i]
}
$sequences = #()
$ranges.GetEnumerator() | Sort-Object -Property Key -Descending | ForEach-Object {
$sequence = $_.Value
$start = $sequence[0]
if ($sequence.Length -gt 1) {
$end = $sequence[-1]
$sequences += "$start-$end"
}
else {
$sequences += $start
}
}
Write-Output ($sequences -join ",")
Output:
1-3,6-9,12-13,15