ConvertTo-Json flattens arrays over 3 levels deep [duplicate] - arrays

This question already has answers here:
Cannot convert PSCustomObjects within array back to JSON correctly
(2 answers)
Closed 6 years ago.
In powershell, the following produces correct results.
$foo = #{}
$foo.bar = #{}
$foo.bar.buzz = #("herp","derp")
ConvertTo-Json $foo
{
"bar": {
"buzz": [
"herp",
"derp"
]
}
}
However if I add one more level, then the array "buzz" is flattened into a string
$foo = #{}
$foo.bar = #{}
$foo.bar.buzz = #{}
$foo.bar.buzz.bazz = #("herp","derp")
ConvertTo-Json $foo
{
"bar": {
"buzz": {
"bazz": "herp derp"
}
}
}
Why does powershell flatten Arrays into strings?
This issue feels similar to the following SO questions, but none of the purposed solutions solves it.
why-does-powershell-silently-convert-a-string-array-with-one-item-to-a-string
what-determines-whether-the-powershell-pipeline-will-unroll-a-collection

From ConvertTo-Json documentation
-Depth
Specifies how many levels of contained objects are included in the
JSON representation. The default value is 2.
Adding -Depth 9 option to ConvertTo-Json fixes the issue
ConvertTo-Json -Depth 9 $foo
Also, there is a difference between ConvertTo-Json $foo and $foo | ConvertTo-Json. Use ConvertTo-Json $foo whenever possible.
convertto-json-an-array-with-a-single-item

Related

Compare duplicates in an array. List values that are contained and not contained in array [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I have these arrays, I want to list unique values. If a value in $arr2 is in $arr1 I want to list that out but only ONE time. If a value in $arr2 is NOT in $arr1 I want to list that out as well. I would like to also add the values in $arr2 that are not contained in $arr1 to a seperate array.
This is written in powershell but if you have a solution/tips in any other language that is perfectly fine with me I will rewrite to powershell.
$arr1 = #(1,1,1,2,2,3,4,4,5,7)
$arr2= #(1,2,3,4,5,6,7)
for ($i = 0; $i -lt $arr2.length; $i++){
for( $j = $i+1; $j -lt $arr1.length; $j++ ){
if($arr2[$i] -eq $arr1[$j]){
Write-Host $arr2[$i]
}
}
}
You can use something like this to create an object which can be used for your need, however it will require further code from your side (i.e.: filtering) to get the results you're looking for.
$arr1 = #(1,1,1,2,2,3,4,4,5,7)
$arr2 = #(1,2,3,4,5,6,7)
$result = [System.Collections.Generic.List[pscustomobject]]::new()
foreach($i in $arr1 + $arr2)
{
if($i -in $result.Value)
{
continue
}
$z = [ordered]#{
Value = $i
Array1 = $false
Array2 = $false
}
if($i -in $arr1)
{
$z.Array1 = $true
}
if($i -in $arr2)
{
$z.Array2 = $true
}
$result.Add([pscustomobject]$z)
}
$valuesOnBothArrays, $valuesOnOneArray = $result.Where({
$_.Array1 -eq $true -and $_.Array2 -eq $true}, 'Split'
)
$valuesOnBothArrays will result in:
Value Array1 Array2
----- ------ ------
1 True True
2 True True
3 True True
4 True True
5 True True
7 True True
$valuesOnOneArray will result in:
Value Array1 Array2
----- ------ ------
6 False True
I suggest using the Compare-Object cmdlet in combination with the .Where() array method:
$arr1 = 1,1,1,2,2,3,4,4,5,7
$arr2 = 1,2,3,4,5,6,7
$inBoth, $uniqueRight =
(Compare-Object -PassThru -IncludeEqual `
($arr1 | Select-Object -Unique) ($arr2 | Select-Object -Unique)).
Where({ $_.SideIndicator -in '==', '=>' }).
Where({ $_.SideIndicator -eq '==' }, 'Split')
"-- in both:"
$inBoth
"-- unique to arr2"
$uniqueRight
Note: Thanks to using Select-Object -Unique on the input arrays so as to only operate on distinct elements, the use of -PassThru with Compare-Object works as expected and passes the integers of interest through directly, rather than as properties of wrapper objects. The caveat is that with [int] array elements specifically, because PowerShell caches values up to 100, having duplicates in either collection would malfunction obscurely. The reason is that -PassThru decorates the pass-through elements with an ETS .SideIndicator property, which affects all uses of a given integer between 0 and 100, so that the .SideIndicator property value of a later duplicate would overwrite the original value - see this answer for more information.
Note:
If you know that the distinct elements of $arr1 are only ever a subset of the ones in $arr2, i.e. that $arr1 contains no unique elements, you can eliminate the intermediate .Where({ $_.SideIndicator -in '==', '=>' }) call.
Unfortunately, as of PowerShell 7.2, the implementation of Select-Object -Unique is quite inefficient - see GitHub issue #11221. If the input arrays were sorted, Get-Unique would be a faster alternative.
The above yields:
-- in both (distinct):
1
2
3
4
5
7
-- unique to arr2
6

In Powershell Why might a foreach-object loop not let you retrieve individual values from an array? [duplicate]

This question already has answers here:
PowerShell outputting array items when interpolating within double quotes
(3 answers)
Closed 2 years ago.
Note the following output.
$Names is an array with two values.
$Machinename is an array with two values.
Their positional values within the array are retrieved accurately outside of a foreach loop.
When fetched within a foreach loop the value requested for the first position in the array i.e. $Names[0] ignores the positional call of [0]..... I need to be able to retrieve that value by itself..... Ultimately I will need to interate through each value to input to a command...
PS C:\Users\lab> $Names
john
jeff
PS C:\Users\lab> $Names[0]
john
PS C:\Users\lab> $Names[1]
jeff
PS C:\Users\lab> $Machinename
dev1
dev2
PS C:\Users\htlab> $Machinename | ForEach-Object { Write-Output "$Names[0]"}
john jeff[0]
john jeff[0]
Sample Script:
$Names = 'john', 'jeff'
$machinename = 'dev1', 'dev2'
$Machinename | ForEach-Object {Write-Output "$Names[0]"}
You're not evaluating the array. By writing "$Names[0]" it is the equivalent of $Names + "[0]".
You need to nest the evaluation inside $(...).
Try this:
$Machinename | ForEach-Object {Write-Output "$($Names[0])"}
That gives me:
john
john
Equally, as pointed out in the comments, this works too:
$Machinename | ForEach-Object {Write-Output $Names[0]}
You could try something like this, as it seemed to work for me, unless thats not what you're asking,
for (($i = 0); $i -lt $names.Count; $i++)
{
$Names[$i] + ":" + $Machinename[$i]
}
Output:
John:dev1
Jeff:dev2

ConvertTo-JSON falsely parses arrays [duplicate]

This question already has answers here:
Unexpected ConvertTo-Json results? Answer: it has a default -Depth of 2
(2 answers)
Closed 2 years ago.
This question has also been answered here:
Stackoverflow: Unexpected ConvertTo-Json results? Answer: it has a default -Depth of 2
GitHub: ConvertFrom-Json and ConvertTo-Json breaks arrays
Mircrosoft Docs: ConvertTo-JSON
TL;DR
If you save your .json with ConvertTo-JSON and it break it, you may want to speccify the -Depth parameter, as it's default value it 2 and it wont parse nestet objects beyond that.
Original Post:
So I want to load, then save data from a JSON file through a PowerShell skript. The JSON contains an array within an object, which looks like this:
{
"head": {
"head2": {
"data0": "a",
"data1": "b"
},
"head3": {
"data8": [
"x",
"y",
"z"
],
"data9": "hello"
}
}
}
Notice the array "data8".
Now when I load and save the file like so: Get-Content test.json | ConvertFrom-JSON | ConvertTo-JSON I want to end up with the exact same file, since I'm not changing anything. However the result is this:
{
"head": {
"head2": {
"data0": "a",
"data1": "b"
},
"head3": {
"data8": "x y z",
"data9": "hello"
}
}
}
The "data8" has been reduced to a single string and I struggle to find out why. It seems to happen during the ConvertTo-JSON because when I don't do that yet, It will give me an array of strings containing x,y and z.
E.g. (Get-Content test.json | ConvertFrom-JSON).head.head3.data8 will result in
x
y
z
and (Get-Content test.json | ConvertFrom-JSON).head.head3.data8 -is [array] gives True
Things I've tried:(get-content test.json | convertfrom-json).head.head3.data8
Various encoding methods in test.json, but this even happens when I save the file as utf8 and add -encoding utf8 to Get-Content so I don't believe that is has to do with that.
For the sake of trying, I even added -compress to ConvertTo-JSON which didn't help either.
Okay so apparently PowerShell only parses data up to a depth of 2 by default. So as pointed out Here you have to specify the depth with the depth prameter of ConvertTo-Json

Powershell: Reformat JSON nested Arrays to Array of Objects

I'm trying to modify a badly formated JSON Textfile, that currently looks like this:
[
["val1", "val2", "val3", "val4"],
["val5", "val6", "val7", "val8"],
["val9", "val10", "val11", "val12"]
]
and I have an other array containing field names
["title1", "title2", "title3", "title4"]
I want to output a final.json textfile looking like this:
[
{"title1": "val1", "title2": "val2", "title3": "val3", "title4": "val4"},
{"title1": "val5", "title2": "val6", "title3": "val7", "title4": "val8"},
{"title1": "val9", "title2": "val10", "title3": "val11", "title4": "val12"}
]
I guess the best way would be to take each row, split by , and then adding them back together foreach-ing over the title names, but I'm not quite sure on how to do this in PowerShell.
Since you're dealing with structured data here, I think the best way is to parse the JSON, and work with the resulting objects. Create the objects you want, then convert back to JSON:
$j1 = #'
[
["val1", "val2", "val3", "val4"],
["val5", "val6", "val7", "val8"],
["val9", "val10", "val11", "val12"]
]
'#
$j2 = #'
["title1", "title2", "title3", "title4"]
'#
$a1 = $j1 | ConvertFrom-Json
$a2 = $j2 | ConvertFrom-Json
0..($a1.Count-1) | ForEach-Object {
$i = $_
$props = #{}
0..($a2.Count-1) | ForEach-Object {
$props[$a2[$_]] = $a1[$i][$_]
}
New-Object PSOBject -Property $props
} | ConvertTo-Json
ConvertTo-Json and ConvertFrom-Json are the cmdlets you need to serialize/deserialize the JSON. Then you just work with the objects.
In this case I'm going through each top level array in $a1 and creating a hashtable that contains the properties you want. Then I'm creating a PSObject with those properties. That gets returned from the ForEach-Object cmdlet (the result is an array of those objects) which then gets piped directly into ConvertTo-Json to give you the output needed.
I think a better approach is to read in the first JSON format via ConvertFrom-Json, then take that array of arrays and for each row, create a PSCustomObject from a hashtable e.g. [PSCustomObject]#{title1=$arr[$row][0]; title2=$arr[$row][1];...}. Once you then have that array of PSCustomObject, convert that back to JSON with ConvertTo-Json.

Getting Missing element from comparison of Array's using powershell [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Comparing two arrays & get the values which are not common
I wanted a logic to get uncommon items from an array, for example:
$a=#(1,2,3,4,5,6)
$b=#(1,2,3,4,5,7,9,10)
I want the output $c to be 6 which is the missing element in $b array, priority should be only given to the array contents of $a.
Can anyone please help me out with this?
Thanks!
Either empo's approach, or
$a1=#(1,2,3,4,5,8)
$b1=#(1,2,3,4,5,6)
Compare-Object $a1 $b1 |
Where-Object { $_.SideIndicator -eq '<=' } |
Foreach-Object { $_.InputObject }
returns 8
$c = $a | ? {!($b -contains $_)}
The priority will be given to the variable you "pipe".

Resources