I am practicing in PowerShell and am making a user response input where one of the options is to input 3 numbers, and the program will return the middle number. I have done this a million times and it seems I cannot get it to return the middle number consistently.
For example when my numbers are 1, 23452342 and 3, it says that 3 is the middle number.
Here is my code:
if ($response -eq 1) {
$a = Read-Host "Enter a number "
$b = Read-Host "Enter a second number "
$c = Read-Host "Enter a third number "
if (($a -gt $b -and $a -lt $c) -or ($a -lt $b -and $a -gt $c)) {
Write-Host "$a is the middle number"
}
if (($b -gt $a -and $b -lt $c) -or ($b -gt $c -and $b -lt $a)) {
Write-Host "$b is the middle number"
}
if (($c -gt $a -and $c -lt $b) -or ($c -gt $b -and $c -lt $a)) {
Write-Host "$c is the middle number"
}
}
Instead of doing a number of individual comparisons simply sorting the three values and picking the second element will give you the median right away. But I suspect what's actually messing up the results for you is that Read-Host returns strings when you need them to be numeric values. Sort order of strings ("1" < "20" < "3") is different from numeric sort order (1 < 3 < 20), because characters at corresponding positions are compared rather than the whole number.
Casting the entered values to integers (or doubles if you expect floating point numbers) should resolve the issue:
if ($response -eq 1) {
[int]$a = Read-Host 'Enter a number'
[int]$b = Read-Host 'Enter a second number'
[int]$c = Read-Host 'Enter a third number'
$n = ($a, $b, $c | Sort-Object)[1]
Write-Host "$n is the median."
}
As an additional solution that would work on any array where u need the middle item you could just solve it like this:
$arr = 1..50
($arr | Sort-Object)[[int](($arr.count -1) /2)]
If your array comes in a format that does not need the sorting, just leave this part out.
edit: Obviously you would have to insert the data into an array on the first step.
Best regards
Related
I am making a random number guessing program where users try to guess a number between 1 and 100 while receiving whether they were higher or lower. one of the stipulations was that there must be a function to quit the program upon the user entering Q or q so I formatted my loop as such:
$RandomNumber = Get-Random -Minimum 1 -Maximum 101
write-output $RandomNumber
$GuessNumber = 0
While ($RandomNumber -ne $Guess)
{
$Guess = Read-Host -Prompt 'Guess a number between 1 and 100 Or enter Q to quit'
if ($Guess -eq 'q' -or 'Q')
{
Write-output "Thank you for playing"
BREAK
}
if ($Guess -gt 100 -or $Guess -le 1)
{
Write-Output 'Invalid Input please enter a number between 1 and 100'
}
if ($Guess -le $RandomNumber)
{
Write-output 'Your number is lower than the hidden number please guess again'
$GuessNumber = $GuessNumber +1
}
if ($Guess -gt $RandomNumber)
{
Write-output 'Your number is higher than the hidden number please guess again'
$GuessNumber = $GuessNumber +1
}
if ($Guess -eq $RandomNumber)
{
$GuessNumber = $GuessNumber +1
Write-output 'Congratulations you guessed the number in' $GuessNumber 'tries'
}
}
the issue I run into is even when the input is equal to 10 for example it still outputs "Thank you for playing" meaning that if loop has taken effect. I am not sure why this occurs as I'm new to powershell and a reasoning for this happening would be helpful
Replace
if ($Guess -eq 'q' -or 'Q')
with
if ($Guess -eq 'q' -or $Guess -eq 'Q')
Your error partially comes from -or 'Q' which evaluates to $true always:
$false -or 'somestring' # => True
And it's also worth noting, -eq is case insensitive ('q' -eq 'Q' is $true).The other error comes from comparing a string with an integer. The output from Read-Host is System.String in this case:
'50' -gt 100 # => True
'50' -lt 100 # => False
On the other hand, you could make your code more readable using a switch statement. For input validation you could use a recursive script block to make sure its always right (between 1 and 100 or Q). To break the outer while loop you can use a labeled break.
$RandomNumber = Get-Random -Minimum 1 -Maximum 101
$validation = {
$choice = Read-Host 'Guess a number between 1 and 100 Or enter Q to quit'
if($choice -notmatch '^([1-9][0-9]?|100|q)$') {
Write-Warning 'Invalid Input, try again.'
return & $validation
}
$choice
}
$attempts = 0
:outer while($true) {
$choice = & $validation
if($choice -eq 'q') {
# End the Game
'Thanks for Playing'
break
}
$attempts++
switch([int]$choice) {
{$_ -gt $RandomNumber} {
"$_ was greater than hidden number"
break
}
{$_ -lt $RandomNumber} {
"$_ was lower than hidden number"
break
}
default {
"You guessed right after $attempts tries, Congratulations!"
break outer
}
}
}
The condition is wrong. $Guess -eq 'q' -or 'Q' returns true always, it should be $Guess -eq 'q' -or $Guess -eq 'Q'.
$StartID = Read-Host -Prompt "StartID"
$StopID = Read-Host -Prompt "StopID"
$i = $StartID
do {
Write-Host $startID
Write-Host $StopID
$i = ($StartId + 1)
} until ($i -gt $StopID)
The first problem is that after the statement $i = $startid + 1, the $i equals 11 and not 2.
The second problem is that even though the until statement says that it should stop when $i -gt $stop the loop continues forever.
How do I get the $i to increase by 1 and not 10 and how do I stop the loop when $i -gt $stop.
Read-Promptreturns a string per default (this stackoverflow answer explains different ways for conversion). You've to convert/cast the string to a numeric value:
[int]$start = Read-Host -Prompt "Start"
[int]$stop = Read-Host -Prompt "Stop"
do {
Write-host $start
$start++
} until ($start -ge $stop)
Hope that helps.
Thanks for the help in this, I think i have over complicated the below but the logic just isn't responding how my mind is telling it too.
Logic in Question:
$a = "One"
$b = "Two"
$c = "Three"
$d = "Four"
If( {$a -and $b} -ne {$c and $d} ) {
Write-Host "Values are Different"
} Else {
Write-Host "values are the same"
}
I want the If statement to run when $a and $b are different to $c and $d, If the are the same see below, I want it to output that the values are the same
$a = "One"
$b = "One"
$c = "One"
$d = "One"
Thanks in advance!
You can use Compare-Object to compare the value pairs as arrays:
if (Compare-Object $a, $b $c, $d -SyncWindow 0) {
'different'
} else {
'same'
}
Note that this is convenient, but relatively slow, which may matter in a loop with many iterations.
The Compare-Object cmdlet compares two arrays and by default returns information about their differences.
-SyncWindow 0 compares only directly corresponding array elements; in other words: $a must equal $c, and $b must equal $d; without -SyncWindow, the array elements would be compared in any order so that 1, 2 would be considered equal to 2, 1 for instance.
Using the Compare-Object call's result as a conditional implicitly coerces the result to a Boolean, and any nonempty result - indicating the presence of at least 1 difference - will evaluate to $True.
As for what you tried:
Use of { ... } in your conditional is not appropriate.
Expressions enclosed in { ... } are script blocks - pieces of code you can execute later, such as with & or .
Even if you used (...) instead to clarify operator precedence (-ne has higher precedence than -and), your conditional wouldn't work as expected, however:
($a -and $b) -ne ($c -and $d) treats all variables as Booleans; in effect, given PowerShell's implicit to-Boolean conversion, you're comparing whether one value pair has at least one empty string to whether the other doesn't.
In addition to the answer from mklement0 and avoiding the rather slow Compare-Object cmdlet:
In what you tried, you will need to compare one specific value with each of the rest of the vales:
($a -eq $b) -and ($a -eq $c) -and ($a -eq $d)
Because the Comparison Operators (-eq) take a higher precedence than the Logical Operators (-and), you can leave the brackets and simplify it to:
$a -eq $b -and $a -eq $c -and $a -eq $d
To make this code DRY and easily expandable for even more values:
if ($a, $b, $c | Where {$_ -ne $d}) {
'different'
} else {
'same'
}
Just remove these {} brackets from the if statement
$a = "One"
$b = "One"
$c = "One"
$d = "One"
If($a -and $b -ne $c -and $d) {
Write-Host "Values are Different"
} Else {
Write-Host "values are the same"
}
Hi I am new to Powershell and need some help:
My Script gets a number by userinput. I want to check this number if it´s between these ranges. So far so easy, but the input from 1-9 is with a leading zero.
With google, I got this working without the special case "leading zero".
do {
try {
$numOk = $true
$input = Read-host "Write a number between 1-12"
} # end try
catch {$numOK = $false }
} # end do
# check numbers
until (
($input -ge 1 -and $input -lt 13) -or
($input -ge 21 -and $input -lt 25) -or
($input -ge 41 -and $input -lt 49) -or
($input -ge 61 -and $input -lt 67) -and $numOK)
Write-Host $input
For example:
Input "5" Output "5"
Input "05" stucks in loop, should be "05"
AddOn: Is it possible to block inputs like 1-9 and just accept inputs like 01-09?
you can use the -f operator to format the string here is to get a 2 digits number
PS>"{0:00}" -f 5
05
some readings : http://ss64.com/ps/syntax-f-operator.html
once again I contact you because I am once again having problems with a Powershell script assignment.
My current assignement is to make a script that lets a user put in 1-10 values. The values have to be positive numbers. After that I have to calculate an average of an array.
My idea in this was the following:
clear-host
$averageArray = #()
Write-Host "How many numbers do you want to put in?" -for yellow
Write-Warning "Maximum is 10!"
Write-Host "Press the Enter key to continue."
$amountValues = Read-host
while($amountVAlues -notmatch "^([1-9]|[1][0])$") {
$amountValues = Read-Host "Please enter a number between 1 and 10"
if($amountVAlues -notmatch "^([1-9]|[1][0])$") {Write-host "Error! Please enter a number between 1 and 10!"}
}
$value1 = read-host "Enter number one"
while($value1 -notmatch '^\d+$') {
$value1 = Read-Host
if($value1 -notmatch '^\d+$') {Write-host "Error! Please enter a positive number!"}
}
$value2 = read-host "Enter number two"
while($value2 -notmatch '^\d+$') {
$value2 = Read-Host
if($value2 -notmatch '^\d+$') {Write-host "Error! Please enter a positive number!"}
}
$averageArray = $averageArray + $value1
write-host "$averageArray"
read-host
I created an array, and made the user input a number between 1-10 for the amount of total $values they want in the array. After that I wanted to loop the input of a $value, and input it in the array. I wanted to loop it as many times as the $amountValues variable.
Problem with doing this is that if I would loop it, $variable1 would get overwritten an 'x' amount of times.
Is there any way to input the values via a loop into the array?
I'ld do it like this:
while ( (1..10) -notcontains $g)
{
$g = read-host "How many numbers do you want to put in? (value from 1 to 10) "
}
$ar=#()
for ($i=0; $i -lt $g; $i++)
{
$ar += read-host "Enter value $($i+1)"
}
$averageArray = ($ar | Measure-Object -Average).average
write-host "Average is : $averageArray"