Home > Back-end >  Powershell - print the list length respectively
Powershell - print the list length respectively

Time:09-21

I want to write two things in Powershell. For example; We have a one list:

$a=@('ab','bc','cd','dc')

I want to write:

1 >> ab
2 >> bc
3 >> cd
4 >> dc

I want this to be dynamic based on the length of the list.

Thanks for helping.

CodePudding user response:

Use a for loop so you can keep track of the index:

for( $i = 0; $i -lt $a.Count; $i   ){
  "$($i   1) >> $($a[$i])"
}

To explain how this works:

  • The for loop is defined with three sections, separated by a semi-colon ;.

    • The first section declares variables, in this case we define $i = 0. This will be our index reference.
    • The second section is the condition for the loop to continue. As long as $i is less than $a.Count, the loop will continue. We don't want to go past the length of the list or you will get undesired behavior.
    • The third section is what happens at the end of each iteration of the loop. In this case we want to increase our counter $i by 1 each time ($i is shorthand for "increment $i by 1")
    • There is more nuance to this notation than I've included but it has no bearing on how the loop works. You can read more here on Unary Operators.
  • For the loop body itself, I'll explain the string

    • Returning an object without assigning to a variable, such as this string, is effectively the same thing as using Write-Output.
      • In most cases, Write-Output is actually optional (and often is not what you want for displaying text on the screen). My answer here goes into more detail about the different Write- cmdlets, output streams, and redirection.
    • $() is the sub-expression operator, and is used to return expressions for use within a parent expression. In this case we return the result of $i 1 which gets inserted into the final string.
      • It is unique in that it can be used directly within strings unlike the similar-but-distinct array sub-expression operator and grouping operator.
      • Without the subexpression operator, you would get something like 0 1 as it will insert the value of $i but will render the 1 literally.
    • After the >> we use another sub-expression to insert the value of the $ith index of $a into the string.
      • While simple variable expansion would insert the .ToString() value of array $a into the final string, referencing the index of the array must be done within a sub-expression or the [] will get rendered literally.

Your solution using a foreach and doing $a.IndexOf($number) within the loop does work, but while $a.IndexOf($number) works to get the current index, .IndexOf(object) works by iterating over the array until it finds the matching object reference, then returns the index. For large arrays this will take longer and longer with each iteration. The for loop does not have this restriction.

Consider the following example with a much larger array:

# Array of numbers 1 through 65535
$a = 1..65535

# Use the for loop to output "Iteration INDEXVALUE"
# Runs in 106 ms on my system
Measure-Command { for( $i = 0; $i -lt $a.Count; $i   ) { "Iteration $($a[$i])" } }

# Use a foreach loop to do the same but obtain the index with .IndexOf(object)
# Runs in 6720 ms on my system
Measure-Command { foreach( $i in $a ){ "Iteration $($a.IndexOf($i))" } }

Another thing to watch out for is that while you can change properties and execute methods on collection elements, you can't change the element values of a non-collection collection (any collection not in the System.Concurrent.Collections namespace) when its enumerator is in use. While invisible, foreach (and relatedly ForEach-Object) implicitly invoke the collection's .GetEnumerator() method for the loop. This won't throw an error like in other .NET languages, but IMO it should. It will appear to accept a new value for the collection but once you exit the loop the value remains unchanged.

This isn't to say the foreach loop should never be used or that you did anything "wrong", but I feel these nuances should be made known before you do find yourself in a situation where a better construct would be appropriate.

CodePudding user response:

Okey,

I fixed that;

$a=@('ab','bc','cd','dc')
$a.Length

foreach ($number in $a) {
    $numberofIIS = $a.IndexOf($number)
    Write-Host ($numberofIIS,">>>",$number)

    }

CodePudding user response:

Bender's answer is great, but I personally avoid for loops if at all possible. They usually require some awkward indexing into arrays and that ugly setup... The whole thing just ends up looking like hieroglyphics.

With a foreach loop it's our job to keep track of the index (which is where this answer differs from yours) but I think in the end it is more readable then a for loop.

$a = @('ab', 'bc', 'cd', 'dc')

# Pipe the items of our array to ForEach-Object
# We use the -Begin block to initialize our index variable ($x)
$a | ForEach-Object -Begin { $x = 1 } -Process {
    # Output the expression
    "$x"   ' >> '   $_

    # Increment $x for next loop
    $x  
}

# -----------------------------------------------------------

# You can also do this with a foreach statement
# We just have to intialize our index variable 
# beforehand

$x = 1
foreach ($number in $a){
    # Output the expression
    "$x >> $number"

    # Increment $x for next loop
    $x  
}
  • Related