0

I created dynamic variable like this :

$Data = @()
$i = 1
Foreach ($S in $SB) {
     New-variable "JR$i" "content"
     New-variable "SN$i" "content"
     New-variable "T$i" "content"
     New-variable "FN$i" "content"
     $i = $i++
}

My goal is to put them in a psobject as follow :

$Data += new-object psobject -property ([ordered]@{JR = "<tr><td>$JR$i</td>"; SN = "<tr><td>$SN$i</td>"; T = "<tr><td>$T$i</td>"; FN = "<tr><td>$FN$i</td>"})
Dasheng
  • 1
  • 1
  • 1
    Right so, whats your question? – Santiago Squarzon Apr 26 '23 at 17:09
  • In your ```new-variable``` lines, Powershell is doing string interpolation to create a variable named, e.g ```JR$i``` -> ```JR1```, but the second example ```$JR$i``` is expanding *two* different variables - ```$JR```-> ```$null``` (because ```$JR``` isn’t defined) and ```$i``` -> ```1``` rather than what you’re probably expecting which is ```$JR1``` -> ```content```. – mclayton Apr 26 '23 at 17:22
  • 1
    You’re going to have to use ```get-variable``` to retrieve the variable values, at which point you might as well just use a hashtable with your names as keys… – mclayton Apr 26 '23 at 17:26
  • 1
    [Try to avoid the the `-Variable` cmdlets for creating dynamic variable names](https://stackoverflow.com/a/68830451/1701026) – iRon Apr 26 '23 at 17:26

1 Answers1

0

Why your current code doesn't work

1. Misuse of the increment operator

$i = $i++ - this doesn't do what you think it does. You probably want $i++ or $i += 1 instead.

What your code is actually doing is complicated. The increment operator documentation says:

The postfix version of the operator increments a variable after its value is used in the statement.

In other words your code $i = $i++ does this:

  • On the right-hand side, evaluate the expression $i ( = 1)
  • Increment $i ($i now equals 2)
  • Assign the previously calculated expression value (1) to the left hand side variable $i
  • $i now equals 1 again!

You can try this yourself:

$i = 1;
$i = $i++;
$i
# 1

You can fix that by using one of these options instead of $i = $i++:

  • $i++;
  • $i += 1;
  • $i = $i + 1;

2. Interpolation problem

In your New-Variable lines, Powershell is doing string interpolation to create a variable named, e.g JR$i -> JR1, but in the second example <td>$JR$i</td> is expanding two different variables - $JR -> $null (because $JR isn’t defined) and $i -> 1 rather than what you’re probably expecting which is $JR1 -> content.

You can test this as well:

$i = 1;
New-Variable -Name "JR$i" -Value "content";

$JR1
# content

"<td>$JR$i</td>"
# <td>1</td>

It's clearer if you enable Strict Mode as you get an error instead:

Set-StrictMode -Version "Latest";

"<td>$JR$i</td>"
# InvalidOperation: The variable '$JR' cannot be retrieved because it has not been set.

What you want instead is "<td>$JR1</td>", but it looks like you need to dynamically generate the variable name because you're looping over multiple values for $i, so you're going to be stuck doing this:

$i = 1;
$jr = (Get-Variable -Name "JR$i").Value;

"<td>$jr</td>"
# "<td>content</td>"

What you can do to fix it

1. Increment properly

This one's easy - just use $i++ or $i += 1 instead

2. Use a hashtable instead

See @iRon's general best-practice advice mentioned in comments for some more background information - I'll apply this to your specific case below.

Instead of creating lots of "dynamic variables", just pack the values into a hashtable using the name as a key - it'll be easier to retrieve the values later...

$SB = @( 1, 2 )
$vars = [ordered] @{};
$i = 1
foreach( $S in $SB )
{
   $vars.Add("JR$i", "content");
   $vars.Add("SN$i", "content");
   $vars.Add("T$i", "content");
   $vars.Add("FN$i", "content");
   $i++
}

$vars
# Name                           Value
# ----                           -----
# JR1                            content
# SN1                            content
# T1                             content
# FN1                            content
# JR2                            content
# SN2                            content
# T2                             content
# FN2                            content

Note - strictly speaking, I'm using an OrderedDictionary instead of a hashtable so the keys get shown in the order they're added, but other than that they're basically the same.

And then to use the hashtable / ordereddictionary:

$i = 1;
foreach( $S in $SB )
{
    "<tr><td>$($vars["JR$i"])</td>";
    $i++
}
# <tr><td>content</td>
# <tr><td>content</td>

However, if you're just trying to generate the pscustomobjects, a cleaner way to do that from your source data might be this, and bypass the entire "dynamic variables" and temporary hashtables / OrderdDictionary completely:

# generate the source data
$SB = @(
  @{
    "JR_Property" = "jr content 1"
    "SN_Property" = "sn content 1"
    "T_Property"  = "t content 1"
    "FN_Property" = "fn content 1"
  },
  @{
    "JR_Property" = "jr content 2"
    "SN_Property" = "sn content 2"
    "T_Property"  = "t content 2"
    "FN_Property" = "fn content 2"
  }
);

$data = $SB | foreach-object {
    [pscustomobject] @{
        "JR" = "<tr><td>$($_.JR_Property)</td>";
        "SN" = "<tr><td>$($_.SN_Property)</td>";
        "T"  = "<tr><td>$($_.T_Property)</td>";
        "FN" = "<tr><td>$($_.FN_Property)</td>"}
}

$data | format-list *
# JR : <tr><td>jr content 1</td>
# SN : <tr><td>sn content 1</td>
# T  : <tr><td>t content 1</td>
# FN : <tr><td>fn content 1</td>
# 
# JR : <tr><td>jr content 2</td>
# SN : <tr><td>sn content 2</td>
# T  : <tr><td>t content 2</td>
# FN : <tr><td>fn content 2</td>
mclayton
  • 8,025
  • 2
  • 21
  • 26