How to Replace Substring in all Elements of Object without Destroying it - jsonata

I need to replace substrings of all elements in an object.
E.g. replace all 'X' in val1 and val2:
{
"input": [
{
"val1": "008 X 148",
"val2": "SOME X DATA"
},
{
"val1": "X 005 5PM",
"val2": "SOME X DATA"
},
{
"val1": "MODTOX",
"val2": "X SOME X DATA"
}
]
}
My first intention was to use $map and then $each, like this:
$map(input, function($i)
{ $each($i, function($s)
{ $replace($s, "X", "Y" )
})
})
, but as expected, this destroys the object.
Any suggestion? Finally 'input' should still be of same structure.

You need to use the transform operator to modify a copy of the input data:
$ ~> | input | $each(function($v, $n){{$n: $replace($v, "X", "Y") }} ) ~> $merge() |
See https://try.jsonata.org/yeKAKg_U_

Related

How do I merge based on a missing elements with JQ?

I have two JSON files that I am working with: dest_sr.json and src_templates.json
What I would like to is create a new JSON object that combines keys from JSON object #1 (specifically the Template_SR key) and JSON object #2 (the Template_Name and Template_ID keys), but only when a VM-Template cannot be found on Pool_ID that matches on both object #1 and object #2
For the two given JSON objects:
dest_sr.json:
{
"Type": "pool",
"Pool_ID": "adb58e84",
"Template_SR": "fc820294"
}
{
"Type": "pool",
"Pool_ID": "d2dea684",
"Template_SR": "313f2a07"
}
src_templates.json:
{
"Type": "VM-template",
"Template_Name": "CentOS 7 CloudInit V1",
"Template_ID": "9b1833a3",
"Pool_ID": "adb58e84"
}
I would like to create a new JSON object that looks like this:
{
"Template_ID",:"9b1833a3",
"Template_SR":"313f2a07",
"Template_Name":"CentOS 7 CloudInit V1",
}
Using Stackoverflow I was able to hack together a hashJoin from examples that looks like this, where it performs a hashJoin on the .Pool_ID attribute to create a new object:
# hashJoin(a1; a2; field) expects a1 and a2 to be arrays of JSON objects
# and that for each of the objects, the field value is a string.
# A relational join is performed on "field".
def hashJoin(a1; a2; field):
# hash phase:
(reduce a1[] as $o ({}; . + { ($o | field): $o } )) as $h1
| (reduce a2[] as $o ({}; . + { ($o | field): $o } )) as $h2
# join phase:
| reduce ($h1|keys[]) as $key
([]; if $h2|has($key) then . + [ $h1[$key] + $h2[$key] ] else . end) ;
hashJoin( $file1; $file2; .Pool_ID)[]
When I call JQ to perform the hashJoin, I get a new object that looks like this:
/tmp $ jq -nc --slurpfile file1 /tmp/dest_sr.json --slurpfile file2 /tmp/src_templates.json -f hashJoinSimple.jq
{
"Type":"VM-template",
"Pool_ID":"adb58e84",
"Template_SR":"fc820294",
"Template_Name":"CentOS 7 CloudInit V1",
"Template_ID":"9b1833a3"
}
Is there any way I can write my merge so that the object would look like this?
{
"Type":"VM-template",
"Pool_ID":"d2dea684",
"Template_SR":"fc820294",
"Template_Name":"CentOS 7 CloudInit V1",
"Template_ID":"9b1833a3"
}
I think given your particular inputs and your desired result, I don't think the hashing is necessary. It seems like all you need to do is select the inputs from dest_sr.json where it doesn't match the id in src_templates.json, then combine it with the desired values from the template.
$ jq --argfile template src_templates.json '
select(.Pool_ID != $template.Pool_ID) + ($template|{Template_Name,Template_ID})
' dest_sr.json

TextMate Grammar - Problem with `end` expression

I'm having huge problems with the end portion of a regex in TextMate:
It looks like end becomes the part of the pattern that's returned between begin and end
Trying to apply multiple endings with one negative lookbehind proves unsuccessful
Here is an example code:
property_name: {
test1: [1, 50, 5000]
test2: something ;;
test3: [
1,
50,
5000
]
test4: "string"
test5: [
"text",
"text2"
]
test6: something2
test7: something3
}
I'm using the following code:
"begin": "\\b([a-z_]+):",
"beginCaptures": {
"1": {
"name" : "parameter.name"
}
}
"end": "(?<!,)\\n(?!\\])",
"patterns": [
{
"name": "parameter.value",
"match": "(.+)"
}
]
My logic for the end regular expression is to consider it ended if there's a new line but only if it's not preceded by a comma (list of values in an array) or followed by a closing square bracket (last value in an array).
Unfortunately it's not working as expected.
What I would like to achieve is that all property_name# and test are matched as parameter.name and the values are matched as parameter.value apart from ;;

jq: recursion -> nested arrays

How can I parse this json structure with jq? It should loop over leafs (projects and groups) recursively.
My use case is: create project and groups in VCS with CLI. Group can have multiple projects, group can be empty, projects must have parent group created in advance.
Similar analogy would be:
group = folder
project = file
path = absolute path in format /root-groups/nested-groups-level-1/nested-groups-level-2/nested-groups-level-N
Thanks
{
"structure":[
{
"name":"rootgroup1",
"type":"group",
"nested":[
{
"name":"nestedproject1",
"type":"project"
},
{
"name":"nestedgroup1",
"type":"group",
"nested":[
{
"name":"nestednestedproject2",
"type":"project"
}
]
}
]
},
{
"name":"rootproject1",
"type":"project"
},
{
"name":"rootgroup2",
"type":"group",
"nested": []
}
]
}
Expected output:
"rootgroup1","group",""
"nestedproject1","project","rootgroup1"
"nestedgroup1","group","rootgroup1"
"nestednestedproject2","group","rootgroup1/nestedgroup1"
"rootproject1","project",""
"rootgroup2","group",""
Try:
jq -r '.structure[] | .. | "\(.name?) \(.type?)"'
Still not sure, how create a parent path.
The following implements a solution to the problem as I understand it:
# $prefix is an array interpreted as the prefix
def details($prefix):
def out:
select(has("name") and has("type")) | [.name, .type, "/" + ($prefix|join("/"))];
out,
if (.nested | (. and length>0))
then .name as $n | .nested[] | details($prefix + [$n])
else empty
end;
.structure[]
| details([])
| #csv
Given your sample input, the output would be:
"rootgroup1","group","/"
"nestedproject1","project","/rootgroup1"
"nestedgroup1","group","/rootgroup1"
"nestednestedproject2","project","/rootgroup1/nestedgroup1"
"rootproject1","project","/"
"rootgroup2","group","/"
This differs in some respects from the sample output, but hopefully you can take it from here.

How could I parse the XML into hash

I hope I can get the list of hashed like that.
Is there any gem can do me a favor ?
Expected result
[
{
"prog_name": "TAIWAN CTA Index",
"prog_id": 9
},
{
"prog_name": "CTO CTA Index",
"prog_id": 12
},
]
Original input file source.xml
<prog>
<prog_name>TAIWAN CTA Index</prog_name>
<prog_id>9</prog_id>
</prog>
<prog>
<prog_name>CTO CTA Index</prog_name>
<prog_id>12</prog_id>
</prog>
...
You should have a look at Nokogiri. Something like:
#doc = Nokogiri::XML(<IO thing here>)
#doc.xpath('prog').map do |prog_element|
{
'prog_name' => prog_element.xpath('prog_name').content,
'prog_id' => prog_element.xpath('prog_id').content
}
end
would do it for you.

Count number of dictionarys in dictionary in swift

I have a buch of accounts stored in a string dictionary and i would like to count the number of accounts existing, so basicly a ".count" but to find the number of dictionaries created.
var dictionary: [String : [ String ]] = ["" : []]
let storeString = "StoreString"
func addUpdateArray(strings: [String], index: Int) {
let locator = storeString + index.description
dictionary[locator] = strings
}
addUpdateArray(["Account1", "Hy"], 1)
addUpdateArray(["Account2", "Hey"], 3)
and now I would like to see how many accounts are have created of the kind dictionary, is ther a way?
Something like this?
var accounts = [String:[String:String]]() // or whatever your structure is
accounts["Edmund"] = [
"amount": "23.87",
"curreny": "dollars"
]
accounts["Baldrick"] = [
"amount": "23.87",
"curreny": "dollars"
]
accounts["Percy"] = [
"amount": "87.00",
"curreny": "peso"
]
println(accounts.keys.array.count) // 3
If you have dictionary of dictionaries and you want to count the number of actual values inside, you can do it like this:
var accounts = [
"accountsGroup1" : ["account1", "account2", "account3", "account4"],
"accountsGroup2" : ["account1", "account2"],
"accountsGroup3" : ["account1", "account2", "account3", "account4"]
]
let accountsCount = accounts.values.map { $0.count }
let numberOfAllAccounts = reduce(accountsCount, 0) { $0 + $1 }
println(numberOfAllAccounts)

Resources