Get index number using value of item in array - arrays

I need to get the index number for an item in array using the items value. I can't seem to see in the documentation how to achieve this.
$json = New-Object Chilkat.JsonObject
$jsonStr = "{ `"id`": 1, `"name`": `"A green door`", `"tags`": [`"home`", 22, `"green`"], `"price`": 125 }"
$success = $json.Load($jsonStr)
if ($success -ne $true) {
$($json.LastErrorText)
exit
}
# Get the "tags" array, which contains "home", 22, "green"
$tagsArray = $json.ArrayOf("tags")
if ($tagsArray -eq $null ) {
$("tags member not found.")
exit
}

Use the JSON code generator at http://tools.chilkat.io/jsonParse.cshtml
For example, if you past the following into the online tool:
{ "id": 1, "name": "A green door", "tags": ["home", 22, "green"], "price": 125 }
You'll get
$json = New-Object Chilkat.JsonObject
# Insert code here to load the above JSON into the json object.
$id = $json.IntOf("id")
$name = $json.StringOf("name")
$price = $json.IntOf("price")
$i = 0
$count_i = $json.SizeOfArray("tags")
while ($i -lt $count_i) {
$json.I = $i
$strVal = $json.StringOf("tags[i]")
$i = $i + 1
}

Related

Creating a 2D Array with escaped characters

I have a CSV, let's call it test.csv
"country","city","population","remarks"
"germany","munih","1472000","i am a
line
brake"
"italy","rome","2873000","<>|'"
"spain","madrid","6642000","!"§$%&/("
I need a full HTML escaped 2D JS-array without headers and names as like below:
"germany","munih","1472000","i am a \nline\nbrake"
respectively:
var MyVar = [["germany","munih","1472000","i am a \nline\nbrake"],["italy","rome","2873000","<>|'"],["spain","madrid","6642000","!§$%&/(""]];
ConvertTo-Json can escape, but did not find any easy way to produce an 2D-Array without Object Names:
Import-Csv ".\test.csv" | ConvertTo-Json -EscapeHandling EscapeHtml
[
{
"country": "germany",
"city": "munih",
"population": "1472000",
"remarks": "i am a \r\nline\r\nbrake"
},
{
"country": "italy",
"city": "rome",
"population": "2873000",
"remarks": "\u003c\u003e|\u0027"
},
{
"country": "spain",
"city": "madrid",
"population": "6642000",
"remarks": "!§$%\u0026/(\u0022"
}
]
ConverTo-CSV does a good job, but can't escape:
Import-Csv ".\test.csv" | ConvertTo-Csv -UseQuotes Always
"country","city","population","remarks"
"germany","munih","1472000","i am a
line
brake"
"italy","rome","2873000","<>|'"
"spain","madrid","6642000","!§$%&/("""
Following routine works. But I believe, there must be easier way.
$t1 = Import-Csv ".\test.csv"
$arr_len = $t1.Count
$obj_len = $t1[0].Psobject.Properties.value.count
$str_builder = [System.Text.StringBuilder]::new()
for ($i=0; $i -lt $arr_len; $i++){
#first outer loop
if ($i -eq 0){[void]$str_builder.Append('var MyVar = [')}
for ($j=0; $j -lt $obj_len; $j++){
#first inner loop
if ($j -eq 0){[void]$str_builder.Append('[')}
#$t1[$i].Psobject.Properties.Name[$j]
$each = $t1[$i].Psobject.Properties.Value[$j]
$each_esc = ([System.Net.WebUtility]::HtmlEncode($each)) -replace("`r`n|`r|'n","\n")
[void]$str_builder.Append('"')
[void]$str_builder.Append($each_esc)
[void]$str_builder.Append('"')
#last inner loop
if ($j -eq ($obj_len-1)){
if ($i -ne ($arr_len-1)){
[void]$str_builder.Append('],')
} else {
[void]$str_builder.Append(']')
}
} else{
[void]$str_builder.Append(',')
}
}
#last outer loop
if ($i -eq ($arr_len-1)){[void]$str_builder.Append('];')}
}
$final = $str_builder.ToString()
[void]$str_builder.Clear()
Any hints, ideas? Thx :)
You could simplify the code like:
Add-Type -AssemblyName System.Web
$data = Import-Csv -Path 'D:\Test\test.csv'
$jsData = foreach ($item in $data) {
# create an array of quoted encoded strings
$values = $item.PsObject.Properties.Value | ForEach-Object {
'"{0}"' -f [System.Web.HttpUtility]::HtmlEncode(($_ -replace '\r?\n', '\n'))
}
# output the encoded strings grouped inside square brackets
'[{0}]' -f ($values -join ',')
}
# combine the resulting data into a 2D array
$result = 'var MyVar = [{0}];' -f ($jsData -join ',')
$result
Output:
var MyVar = [["germany","munih","1472000","i am a \nline\nbrake"],["italy","rome","2873000","<>|'"],["spain","madrid","6642000","!§$%&/(""]];
Right from the given input it isn't possible to produce the required output.
Youe have to create a nested list first, then the json document.
Try the following:
$list = [System.Collections.Generic.List[psobject]]::new()
foreach($item in $input) {
$nestedList = [System.Collections.Generic.List[string]]::new()
$itemProps = $item.psobject.properties
foreach($itemProp in $itemProps) {
$nestedList.Add($itemProp.Value)
}
$list.Add($nestedList)
}
$list | ConvertTo-Json -EscapeHandling EscapeHtml
<#
$PSVersionTable.PSVersion
Major Minor Patch PreReleaseLabel BuildLabel
----- ----- ----- --------------- ----------
7 1 0
#>
$fieldsCount = 4
$rawText = #'
"country","city","population","remarks"
"germany","munih","1472000","i am a
line
brake"
"italy","rome","2873000","<>|'"
"spain","madrid","6642000","!"§$%&/("
'#
$rawArray = $rawText.Trim("`"") -split "`r`n" -join "," -split '","'
$table = $(for ($i = $fieldsCount; $i -lt $rawArray.Count; $i += $fieldsCount){
[PSCustomObject]#{
country = $rawArray[$i]
city = $rawArray[$i+1]
population = $rawArray[$i+2]
remarks = $rawArray[$i+3]
}
})
$table | ConvertTo-Json
<#
country city population remarks
------- ---- ---------- -------
germany munih 1472000 i am a ,line,brake
italy rome 2873000 <>|'
spain madrid 6642000 !"§$%&/(
[
{
"country": "germany",
"city": "munih",
"population": "1472000",
"remarks": "i am a ,line,brake"
},
{
"country": "italy",
"city": "rome",
"population": "2873000",
"remarks": "<>|'"
},
{
"country": "spain",
"city": "madrid",
"population": "6642000",
"remarks": "!\"§$%&/("
}
]
#>

How to update values from a JSON input

I had another question that I ended up solving myself where I take a JSON input that has names and IP addresses. Then I resolve those IP addresses by looping through and need to replace the IP address with the resolve FQDN if there is one
I have no idea how to update/replace these values from the original JSON. I’ve read that arrays cannot be changed, only added to. This is where I’m stuck as I can get my script to write-out the resolve FQDN is there was one or the IP if there wasn’t... but I can’t get these values to replace the original value from the JSON with the ultimate goal to then take the newly modified JSON and upload it as a new config
Sample JSON input
{
"entry": [
{
"#name": "31.170.162.203",
"ip-netmask": "31.170.162.203",
"description": "test1"
},
{
"#name": "37.193.217.222",
"ip-netmask": "37.193.217.222",
"description": "test2"
},
{
"#name": "46.17.63.169",
"ip-netmask": "46.17.63.169",
"description": "test3"
}
]
}
$input = Get-Content 'C:\Users\e\Desktop' -raw | ConvertFrom-Json
$iplist = $input.entry.'ip-netmask'
foreach ($ip in $iplist) #for each line in the file...
{
$hostnames = $null
try {
$hostnames = [System.Net.Dns]::GetHostByAddress("$ip").Hostname #...resolve the ip
}
catch [System.Management.Automation.MethodInvocationException] {
$hostnames = "Server IP cannot resolve."
}
catch {
$hostnames = "unknown error."
}
if ($hostnames -ne "Server IP cannot resolve.") {
$ip -replace $ip, $hostnames
} else {
Write-Host $ip
}
}
Your json had an extra comma. I would do it this way. A property with a dash is harder to work with.
$a = cat file.json | convertfrom-json
$a.entry | foreach {
if ($namehost = (resolve-dnsname $_.'ip-netmask').namehost ) { # not null
$_.'ip-netmask' = $namehost
}
}
$a.entry
#name ip-netmask description
----- ---------- -----------
31.170.162.203 31.170.162.203 test1
37.193.217.222 l37-193-217-222.novotelecom.ru test2
46.17.63.169 46.17.63.169 test3
This is sort of like saying:
$namehost = (resolve-dnsname $_.'ip-netmask').namehost
if ($namehost -ne $null) { # ...
# or
if ($namehost) { # ...
but I'm doing the assignment and testing the value of the assignment at the same time, like in C. An assignment can be an expression.
$a = ($b = 1)
Then I'm going through the "entry" array and assigning each 'ip-netmask' property to the results if they aren't null.
The following code snippet could help:
$Json = #'
{
"entry":[
{
"#name":"31.170.162.203",
"ip-netmask":"31.170.162.203",
"description":"test1"
},
{
"#name":"37.193.217.222",
"ip-netmask":"37.193.217.222",
"description":"test2"
},
{
"#name":"46.17.63.169",
"ip-netmask":"46.17.63.169",
"description":"test3"
}
]
}
'# | ConvertFrom-Json
for ( $i = 0; $i -lt $Json.entry.Count; $i++ ) {
$entry = $Json.entry[$i]
$ip = $entry.'ip-netmask'
$hostnames = $null
try {
$hostnames = [System.Net.Dns]::GetHostByAddress("$ip").Hostname #...resolve the ip
$entry.'ip-netmask' = $hostnames
}
catch [System.Management.Automation.MethodInvocationException] {
$hostnames = "Server IP cannot resolve."
}
catch {
$hostnames = "unknown error."
}
Write-Host $entry.'#name', $hostnames -ForegroundColor Cyan
}
### debugging output:
$Json.entry
### final conversion hinted:
### $Json | ConvertTo-Json
###
Note that I use a here-string instead of (probably incorrect)
Get-Content 'C:\Users\e\Desktop' -raw

powershell csv to json with multiple level of array

Currently I am fetching the data from flat CSV and converting it to json. I am able to take it two level down with three Arrays like below
The CSV file contains the following data.
The code I have written to achieve the above is using array,powershell object and convertto-json
$ArrayAPPName = #()
$ArrayAPProles = #()
$Arrayserver= #()
$i = 0
foreach ($item in $CSV) {
if ($i -eq 0) {
$JSONappname = $($item.'AppName')
$JSONapprole = $($item.'ROLE')
}
if ($($item.'AppName') -ne $JSONappname) {
$ArrayAPPName += [pscustomobject]#{
AppName = $JSONappname;
Roles = $ArrayJSONroles
}
}
$ArrayJSONserver = [pscustomobject]#{
CPU = $item.CPU;
RAM = ([int]$item.Memory);
}
if ($($item.'AppName') -eq $JSONappname)
{
[array]$ArrayJSONroles += [pscustomobject]#{
Role = $($item.'ROLE');
Server = $ArrayJSONserver
}
}
else {
[array]$ArrayJSONroles = [pscustomobject]#{
Role = $($item.'ROLE');
Server = $ArrayJSONserver
}
}
$JSONappname = $($item.'AppName')
$i++
}
$ArrayAPPName += [pscustomobject]#{
Name = $JSONappname;
Roles= $ArrayJSONroles
}
$ArrayAPPName = [pscustomobject]#{
Apps = $ArrayAPPName | Select-Object -Property Name, Roles
}
$ArrayAPPName = $ArrayAPPName | ConvertTo-Json -Depth 10
However, Now we have situation where for same role we can have multiple
server request. so for each application name, we can have multiple application
role and for each application role we can have multiple Server.
{
"Apps": [
{
"AppName": "ABC",
"Roles": [
{
"Role": "App",
"Server": [
{
"CPU": "2",
"Memory": "1024",
},
{
"CPU": "2",
"Memory": "2084",
}
]
}
]
}
]
}

How to get index of hashtable in array?

I'm having a little trouble finding the index of a hashtable in an array. I create a JSON with this code:
$start = {
Clear-Host
$BIB = Read-Host 'Bibliothek'
$BIBName = Read-Host 'Bibliothek Name'
$Standort = Read-Host 'Bibliothek Standort'
$Autor = Read-Host 'Buchautor'
$BuchName = Read-Host 'Buchname'
$jsonfile = "C:\Skripte\bibV2-1000.xml"
if(![System.IO.File]::Exists($jsonfile)){
$Data = #{BIBs = #(
#{$BIB = #{BIBName=$BIBName},
#{Standort = $Standort},
#{Bücher = #(
#{BuchName = $BuchName;
Autor = $Autor})
}}
)}
ConvertTo-Json -Depth 50 -InputObject $Data | Add-Content $jsonfile
.$continue
} else {
$jsonfile = "C:\Skripte\bibV2-1000.json"
$Data = Get-Content $jsonfile | ConvertFrom-Json
$Data.BIBs += New-Object -TypeName psobject -Property #{$BIB =
#{BIBname=$BIBName},
#{Standort=$Standort},
#{Bücher = #(#{
Buchname=$BuchName;
Autor=$Autor})
}
}
ConvertTo-Json -Depth 50 -InputObject $Data | Out-File $jsonfile}
.$continue
}
$continue = {
Write-Host ""
Write-Host "Was wollen Sie machen?"
Write-Host "(1) Eine weitere Bibliothek hinzufügen"
Write-Host "(2) Einer Bibliothek neue Bücher hinzufügen"
Write-Host "(E) Script beenden"
If (($read = Read-Host ) -eq "1") {
&$start} else {
if (($read) -eq "2") {
. C:\Skripte\büc.ps1 } else {
if (($read) -eq "E") {
exit} else {
Write-Host "+++ FALSCHE EINGABE! Bitte wählen Sie (1) oder (2) für die entsprechende Aktion +++"
.$continue
}
}
}
}
&$start
The output is as follows:
{
"BIBs": [{
"BIB1": [{
"BIBName": "123"
},
{
"Standort": "123"
},
{
"Bücher": [{
"Autor": "123",
"BuchName": "123"
}]
}
]
},
{
"BIB2": [{
"BIBname": "345"
},
{
"Standort": "345"
},
{
"Bücher": [{
"Autor": "345",
"Buchname": "345"
}]
}
]
}
]
}
Now I want to find out the index of "BIB1". I already tried the IndexOf()-Method which should create the output "0" but it gives me "-1" instead, because it can't find the value. How can I get the index of "BIB1"?
Judging by your earlier question, you're attempting to get the index of a specific object so you can access it via its containing array. However, you can do this more directly: $objOfInterest = $Data.BIBs | ? BIB1 - see my answer to your earlier question for details.
You need to iterate over the array elements of $Data.BIBs, which - on reading your serialized-to-a-file-as-JSON hashtables back in with ConvertFrom-Json - are custom objects (as Ansgar correctly points out; they are instances of [System.Management.Automation.PSCustomObject]), and check each for the presence of property 'BIB1':
(In a hashtable, you'd check for the presence of key 'BIB1' with .ContainsKey('BIB1'))
To test the existence of an object property, you need reflection, which is most easily - but somewhat obscurely - achieved via the hidden .PSObject property, as demonstrated in Ansgar Wiechers' more elegant solution.
However, given that the properties of interest have nonempty values, we can infer from the presence of a nonempty value that a given property exists, using implicit Boolean (logic): $obj.'BIB1' by default returns $null if there is no BIB1 property, which is "falsy" in a Boolean context such as an if conditional; conversely, any nonempty value is "truthy":
$propName = 'BIB1'
$i = $ndx = -1
foreach ($obj in $Data.BIBs) {
++$i
if ($obj.$propName) { $ndx = $i; break}
}
$ndx # $ndx now contains the index of the target object or -1 if there was no match
$Date.BIBs is an array of custom objects, not hashtables (since you wrote your original data to a JSON file and then converted that back), so you need something like this:
$arr = $Data.BIBs | ForEach-Object { $_.PSObject.Properties.Name }
$arr.IndexOf('BIB1') # returns 0
$arr.IndexOf('BIB2') # returns 1

Powershell Switch - Set amount of choices based of the amount of Array elements

I'm trying to create an "switch" statement that will have amount of choices based from the amount of returned objects.
First Example:
[array]$A1 = ("QWERTY", "ASDFGH")
so the "switch" function will look like this:
Switch ($Login = read-Host -Prompt Login) {
1 { $Login = "QWERTY" }
2 { $Login = "ASDFGH" }
}
Second Example:
[array]$A2 = ("A", "B", "C", "D")
so the "switch" function will look like this:
Switch ($Login = read-Host -Prompt Login) {
1 { $Login = "A" }
2 { $Login = "B" }
3 { $Login = "C" }
4 { $Login = "D" }
}
So i was thinking about some foreach loop to create simple string and then run command from string. I mange to do this, but I'm unable to run my generated string as command:
[array]$AR1 = ("QWERTY", "ASDFGH")
$q1 = "switch (Read-Host -Prompt Login) {"
$q2 = " }"
$Number = 0
$Script:ArrayFull = $null
$AR1 | % {
$_
$ArrayElement = $null
$NameOfTheLoginVariable = '$Script:User_Login'
$NumberOfArrayElements = $AR1.count
if ($NumberOfArrayElements -ne 0) {
$Number = $Number+1
$NumberOfArrayElements = $NumberOfArrayElements-1
$ArrayElement = "$Number { $NameOfTheLoginVariable = '$_' }"
$ArrayElement
$Script:ArrayFull += $ArrayElement
}
}
$SwitchCommand = $q1+$ArrayFull+$q2
$SwitchCommand = $SwitchCommand.ToString()
$Test1 = "switch (Read-Host) {1 { $Script:User_Login = 'QWERTY' }1 { $Script:User_Login = 'ASDFGH' } }"
#switch (Read-Host -Prompt Login) {1 { $Script:User_Login = 'QWERTY' }1 { $Script:User_Login = 'ASDFGH' } }
#& $Test1
& $SwitchCommand
But even if this code produce correct "Switch" statement as string, i can't execute it. Why ?
Anyway, this method is really ugly so maybe there is a better one ?
That switch statement is composed of script blocks. You can't just substitute a string for them.
You can use [scriptblock]::create() to create new script blocks from the string, but in this application I think I'd just work up an ordered hash table of script blocks. Then you have keys to present in the manu that will map to the script block that needs to run, and an array index to match to the number of choices you need to present.
While it is possible that with full context of the problem domain #Shay's solution may be necessary, the stated examples are simply asking for an array lookup so the same result may be achieved with this:
[array]$a = "A", "B", "C", "D"
[int]$login = read-host -prompt login
$selected = & {if ($login -ge 0 -and $login -lt $a.Length) { $a[$login-1] }}
So here again if you enter 2 you will get "B". Just as in the stated examples and in Shay's solution, if you enter a value out of bounds you do not get a returned value.
Give this a try, entera number between 1-4 whyen prompted:
[array]$a = "A", "B", "C", "D"
$login = read-host login
$switch = 'switch($login){'
for($i=1;$i -le $a.length; $i++)
{
$switch += "`n`t$i { '$($a[$i-1])'; break }"
}
$switch += "`n}"
Invoke-Expression $switch

Resources