I have an if statement inside a foreach inside a foreach. Result is this a list of numbers one in a line. Example:
3
2824
28
6374
3887
198
1953
3827
I’d like to have this as a result in a variable instead:
"watchers": ["3", "2824", "28", "6374", "3887", "198", "1953", "3827"],
I currently have no real idea how to convert the foreach results to the example string.
Powershell preferred but I have access to pretty much any language.
Edit:
Rephrasing my question, and/or to be more specific, I have this CSV
user_id,ticket_id
253,24704
1257,24201
1257,17038
1257,22560
1257,22547
1925,24783
2452,24766
2452,24787
2453,24766
2453,24787
I’m trying to write a script that creates this JSON for me:
[{
“ticket_id”: “253”,
“watchers”: [“24704”]
}, {
“ticket_id”: “1257”,
“watchers”: [“24201”, “17038”,“22560”,“22547”]
}, {
“ticket_id”: “1925”,
“watchers”: [“24783”]
}, {
“ticket_id”: “2452”,
“watchers”: [“24766”,“24787”]
}, {
“ticket_id”: “2453”,
“watchers”: [“24766”,“24787”]
}
]
What you seem to be after is a Json string with a property called watchers
whose value is an array of numbers, in which case constructing it manually is just wrong, you can use ConvertTo-Json
, example:
$someNumbers = 0..10
$filteredNumbers = foreach ($number in $someNumbers) {
if ($number -gt 5) {
$number.ToString()
}
}
ConvertTo-Json @{ watchers = $filteredNumbers } -Compress
# Outputs: {"watchers":["6","7","8","9","10"]}
If you wanted something exactly like the output you have in question and want to construct the string manually, you can first join the array of numbers with a delimiter ", "
and then interpolate that string in a new string:
$numbersString = $filteredNumbers -join '", "'
'"watchers": ["{0}"],' -f $numbersString
# Outputs: "watchers": ["6", "7", "8", "9", "10"],
Based on comments and looking at the last edit on your question, assuming the CSV is in a file, you can use Group-Object
on user_id
:
Import-Csv pathtothefile.csv |
Group-Object user_id |
ForEach-Object {
[pscustomobject]@{
ticket_id = $_.Name
watchers = @($_.Group.ticket_id)
}
} | ConvertTo-Json
The output from the above using the sample CSV would be:
[
{
"ticket_id": "1257",
"watchers": [
"24201",
"17038",
"22560",
"22547"
]
},
{
"ticket_id": "1925",
"watchers": [
"24783"
]
},
{
"ticket_id": "2452",
"watchers": [
"24766",
"24787"
]
},
{
"ticket_id": "2453",
"watchers": [
"24766",
"24787"
]
},
{
"ticket_id": "253",
"watchers": [
"24704"
]
}
]
6
If a file named C:Testfile.txt
contains the following:
3
2824
28
6374
3887
198
1953
3827
You can write PowerShell as follows to get the resulting output:
$filePath = "C:Testfile.txt"
# Empty Array
$watchers = @()
foreach ($line in Get-Content -Path $filePath) {
$watchers += $line
}
$json = @{ watchers = $watchers } | ConvertTo-Json
# Print
$jsonString = $json | Out-String
$jsonString
Result is
{
"watchers": [
"3",
"2824",
"28",
"6374",
"3887",
"198",
"1953",
"3827"]
}
The PowerShell version used is 7.4.5.
1
Worth mentioning that PowerShell has three different ForEach
es. You should use the one that makes the most sense.
There’s the imperative language construct ForEach ($var in list) { ... }
:
PS> ForEach ($var in $someList) { doSomething -with $var }
Then there’s ForEach-Object
, which can be abbreviated to just ForEach
(or to %
for quick one-liners at the prompt), which is used in pipelines; it’s the equivalent of the map
primitive in functional languages:
PS> $someList | ForEach-Object { doSomething -with $_ }
Finally, collection types have a ForEach
method that you can call on them with a block of code to run for each item:
PS> $someList.ForEach({ doSomething -with $_ })
All of these return a list of the results, though the imperative statement isn’t always usable as an expression; you can’t, for example, put it in parentheses. That limits its embeddability syntactically.
Regardless of which one you use, though, none of them has any idea about “lines”; they just generate a list. Sure, if you don’t do anything with that list, then PowerShell will by default print it to your terminal with one element per line. But that comes after the ForEach
is done, and PowerShell does the same thing with any list, including a literal one you simply type at the prompt:
PS> @(3,2824,28,6374,3887,198,1953)
3
2824
28
6374
3887
198
Since ForEach
returns a list, you can assign it to a variable directly:
PS> $myList = ForEach code goes here
But the value of that variable is still a list, not a string of quoted values in square brackets.
It looks like what you want is the JSON representation of an object (which in PowerShell is an associative array/hash table) with a “watchers” property whose value is a list of strings. You can create such an object easily:
PS> $myObj = @{ watchers = $myList | % { $_.ToString() } }
Above I used another ForEach – ForEach-Object
under its alias of %
– to convert each of the numbers in the input list to a string; the change isn’t visible in PowerShell’s default output, but will make a difference in the form of quotation marks in the generated JSON. It would make more sense to call .ToString()
as part of the body of your original loop(s); I didn’t show that since you didn’t share that code.
Anyway, once you have the object, you can convert it to JSON like so:
PS> $myObj | ConvertTo-Json -Compress
{"watchers":["3","2824","28","6374","3887","198","1953"]}
Of course, you can do that all at once without the intermediate variables:
PS> @{ watchers = ForEachCode with ToString() } | ConvertTo-JSON -Compress
{"watchers":["3","2824","28","6374","3887","198","1953"]}