I have an array, but I need to restructure it. I can only do this in twig.
Original array element looks like $arr[0]:
"_id" => array:3 [
"cityName" => "someCityName"
"id" => 111
"className" => "someClassName"
]
"count" => 85
my result array element should look like $arr[0]:
"someCityName" => [
12 => [
"someClassName" => 32,
"someOtherClassName" => 44
]
]
in php I would just do
$arr[$cityName][$id][$className] = $count;
And this would work, but this must be done in twig.
I was hoping to use twig merge, but when I try doing
{% for infoArr in result %}
{% set cityName = infoArr['_id']['cityName'] %}
{% set id = infoArr['_id']['id'] %}
{% set class = infoArr['_id']['className'] %}
{% set countCity.cityName = countCity.cityName|merge({(id):([])}) %}
{% endfor %}
or
{% set countCity[cityName] = countCity[cityName]|merge({(id):([])}) %}
the error is Unexpected token "punctuation" of value "[" ("end of
statement block" expected)
.
How should this be done in twig correctly?
If you wanted to do something like this in twig, which I'm not encouraging, you need to reconstruct every part of the array. Also note you can't use merge with numeric indeces, otherwise the data will be appended, not overwritten. More information on this issue here
$data = [
'data' => [
[
'_id' => [
'cityName' => 'foo',
'className' => 'alpha',
'id' => 42,
],
'count' => 40,
],
[
'_id' => [
'cityName' => 'bar',
'className' => 'alpha',
'id' => 55,
],
'count' => 123,
],
[
'_id' => [
'cityName' => 'foo',
'className' => 'beta',
'id' => 42,
],
'count' => 99,
],
],
];
{% set output = {} %}
{% for raw in data %}
{% if not attribute(output, raw._id.cityName) is defined %}
{% set output = output|merge({ (raw._id.cityName) : {}, }) %}
{% endif %}
{% if not attribute(output[raw._id.cityName], 'id_'~raw._id.id) is defined %}
{% set output = output|merge({(raw._id.cityName) : output[raw._id.cityName] | merge({('id_'~raw._id.id):[], }) }) %}
{% endif %}
{% set output = output|merge({(raw._id.cityName) : output[raw._id.cityName] | merge({('id_'~raw._id.id): output[raw._id.cityName]['id_'~raw._id.id]|merge({(raw._id.className): raw.count}), }) }) %}
{% endfor %}
{{ output.foo.id_42.alpha }} {# output: 40 #}
demo
Related
I have in my ansible playbook a dict that looks similar to:
"fact_one": true,
"fact_two": true,
"disks": [
{
"name": "/dev/sda"
},
{
"name": "/dev/sdb"
},
{
"name": "/dev/sdc"
}
],
}
So, now i need to do a loop through the disks items and then update each disks entry with a new variable called ID. So, my new dict should looks like:
"fact_two": true,
"disks": [
{
"name": "/dev/sda"
"id": "id001"
},
{
"name": "/dev/sdb"
"id": "id002"
},
{
"name": "/dev/sdc"
"id": "id003"
}
],
}
Also, as you can see, my ID is a counter that should be added in +1 each iteration.
How I can write this kind of loop in Ansible ?
I want to update the existing facts and not to create new ones.
One solution is this one:
set_fact:
disks: |
{% set result = [] %}
{% for d in disks %}
{% set _ = result.append('name': d.name, 'id': 'id00' + loop.index | str) %}
{% endfor %}
{{ result }}
or
set_fact:
disks: |
{% set result = [] %}
{% for d in disks %}
{% set item = d %}
{% set _ = item.update('id': 'id00' + loop.index) | str ) %}
{% set _ = result.append(item) %}
{% endfor %}
{{ result }}
I am a complete newcomer to Liquid, started playing with it a few hours ago.
i am trying to parse a JSON and extract certain key value pairs out of it, yet i am not successful at all. the JSON is outputed from Azure Form Recognizer, and i am trying to do a Liquid json 2 json in Logic Apps.
the JSON looks like this
{
"status": "success",
"pages": [
{
"number": 1,
"keyValuePairs": [
{
"key": [
{
"text": "Page No:",
"boundingBox": [
507.4
]
}
],
"value": [
{
"text": "1",
"boundingBox": [
586.8
],
"confidence": 1.0
}
]
},
....
...
what i am looking for is to loop through the keyValuePairs array, and then pull the "text" element from the "key" array, and the "text"+"confidence" element from the "value"array.
Could you please help me in understanding the approach here? I tried something like this, but the array comes back empty
{
"results":[
{% for kvp in content.keyValuePairs %}
{% for mykey in kvp.key %}
"thiskey":"{{mykey.text}}
{%- endfor -%}
{% for myval in kvp.value %}
"thisvalue":{{myval.text}}
"thisconfidence":{{myval.confidence}}
{%- endfor -%}
{%- endfor -%}
]
}
And second question: what's the best approach for debugging Liquid, so i can see where i am getting it wrong and adjust?
Thank you!
According to the sample of json data and the liquid template you provided, I know there are more than one object under the property keyValuePairs in your json data. So I think using "Parse JSON" action is not a good solution for it, we'd better use liquid template as you mentioned in your question. But I'm confused about that if there are more than one object under the property pages in your json data ?
Now I assume the json data just has one object under the property pages and provide the solution of liquid template for your reference (If the json data has more than one object under the property pages, please provide some more details of your requirements and I will modify my solution).
I assume your json data like below:
{
"status": "success",
"pages": [
{
"number": 1,
"keyValuePairs": [
{
"key": [
{
"text": "Page No:",
"boundingBox": [
507.4
]
}
],
"value": [
{
"text": "1",
"boundingBox": [
586.8
],
"confidence": 1.0
}
]
},
{
"key": [
{
"text": "Page No:",
"boundingBox": [
507.4
]
}
],
"value": [
{
"text": "2",
"boundingBox": [
586.8
],
"confidence": 2.0
}
]
}
]
}
]
}
1. We need to get the object under the property pages and put it in the "content" of "Transform JSON to JSON" action. We can parse the whole json data(with "Parse JSON" action) first and do something like below:
2. Upload the liquid template to integration account, please refer to my liquid template:
{
"results":[
{% for kvp in content.keyValuePairs %}
{%- if forloop.Last == true -%}
{
{% for mykey in kvp.key %}
"thiskey": "{{mykey.text}}",
{%- endfor -%}
{% for myval in kvp.value %}
"thisvalue": "{{myval.text}}",
"thisconfidence": "{{myval.confidence}}"
{%- endfor -%}
}
{%- else -%}
{
{% for mykey in kvp.key %}
"thiskey": "{{mykey.text}}",
{%- endfor -%}
{% for myval in kvp.value %}
"thisvalue": "{{myval.text}}",
"thisconfidence": "{{myval.confidence}}"
{%- endfor -%}
},
{%- endif -%}
{%- endfor -%}
]
}
3. After running this logic, we can get the result as:
{
"results": [
{
"thiskey": "Page No:",
"thisvalue": "1",
"thisconfidence": "1"
},
{
"thiskey": "Page No:",
"thisvalue": "2",
"thisconfidence": "2"
}
]
}
Something you need to be aware of:
Since we put the object under the property pages to the content of "Transform JSON to JSON" action, so the content in the liquid template represents it.
In liquid template, we need to be careful about the comma character, so I use {%- if forloop.Last == true -%} to judge if it is the last object in the loop, and then judge if we need to add the comma character.
For your second question about what's the best approach for debugging Liquid ?, unfortunately I think we can just complete the liquid template and upload to the integration account, then we can see the result. I don't know if there is a way for us to debug it easily.
Hope it helps~
I have tried to design workflow using your sample JSON payload.
I have parsed the JSON schema and extracted the required values from it.
Since your JSON payload is a bit complex, we need to use Logic App Expressions to extract the value out of it.
Payload I used:
{
"status": "success",
"pages": [{
"number": 1,
"keyValuePairs": [{
"key": [{
"text": "Page No:",
"boundingBox": [
507.4
]
}],
"value": [{
"text": "1",
"boundingBox": [
586.8
],
"confidence": 1.0
}]
},
"null"
]
}]
}
Once you get the JSON output from the upstream actions, you have to parse it.
Now, Initialize a couple of variables to get the required value from the parsed JSON.
Expression to be used in the first variable: concat(body('Parse_JSON')?['pages'][0]?['keyValuePairs'][0]?['key'][0]?['text'],body('Parse_JSON')?['pages'][0]?['keyValuePairs'][0]?['value'][0]?['text'])
Expression to be used in the second variable: body('Parse_JSON')?['pages'][0]?['keyValuePairs'][0]?['value'][0]?['confidence']
Finally, the logic apps after your Form Recognizor will look something like this:
I hope it helps!
I got an issue recently and I can’t resolve it on my own.
In my code $locale returns current language, the default is en
{{ $locale := .Site.Language.Lang }}
following that, I checked that outputs of two following expressions
{{ (index .Site.Data $locale).nav.sidebar.navItems }}
{{ (index .Site.Data "en").nav.sidebar.navItems }}
return correctly the same data.
sidebar.json file looks like this:
{
"navItems": [
{
"id": 1,
"title": "Parent",
"url": "/",
"parent": null
},
{
"id": 2,
"title": "Child",
"url": "/",
"parent": {
"id": 1
}
},
...
]
}
In code below
{{ $menus := (index .Site.Data $locale).nav.sidebar.navItems }}
{{ $mainMenus := where $menus "parent" nil }}
I get error error calling where: can't iterate over <nil>
but when I change it for
{{ $menus := (index .Site.Data "en").nav.sidebar.navItems }}
result is as expected.
I don't want to have languages hardcoded due to other languages I will use in my app.
I created such workaround:
{{ $locale := .Site.Language.Lang }}
{{ $menus := (index .Site.Data $locale).nav.footer.navItems }}
{{ $mainMenus := slice }}
{{ $subMenus := slice }}
{{ range $menus }}
{{ if (isset .parent "id") }}
{{ $subMenus = $subMenus | append . }}
{{ else }}
{{ $mainMenus = $mainMenus | append . }}
{{ end }}
{{ end }}
but I'm still confused why locale variable didn't work with where function.
I'm working with prestashop and try to get value from a form with checkbox using a HelperForm
SO what I had is :
$fields_form[0]['form']= [
'legend'=> [
'title'=> $this->l('Indexation')
] ,
'input'=>[
[
'type'=>'text',
'label'=> $this->l('Base(s) à indexer'),
'name'=>'options',
'size'=>20,
'required'=>true
]
],
'submit'=>[
'title' => $this->l('Save'),
'class' => 'btn btn-default pull-right'
]
];
and then
$helper = new HelperForm();
[...]
$helper->toolbar_btn = array(
'save' =>
array(
'desc' => $this->l('Save'),
'href' => AdminController::$currentIndex.'&configure='.$this->name.'&save'.$this->name.
'&token='.Tools::getAdminTokenLite('AdminModules'),
),
'back' => array(
'href' => AdminController::$currentIndex.'&token='.Tools::getAdminTokenLite('AdminModules'),
'desc' => $this->l('Back to list')
)
);
// Load current value
$helper->fields_value['options'] = Configuration::get('options');
return $helper->generateForm($fields_form);
and in my getContent I had :
$my_module_name = strval(Tools::getValue('options'));
return $my_module_name;
So until there I had no problem. I write 'test' in the text input and then 'test' is returned but I don't want a text input I want a checkbox input so I changed my form for :
$fields_form[0]['form']= [
'legend'=> [
'title'=> $this->l('Indexation')
] ,
'input'=>[
[
'type'=>'checkbox',
'label'=> $this->l('Base(s) à indexer'),
'name'=>'options',
'required'=>true,
'values'=>[
'query'=>$options,
'id'=>'id',
'name'=>'name'
]
]
],
'submit'=>[
'title' => $this->l('Save'),
'class' => 'btn btn-default pull-right'
]
];
and $options is :
$options = [
[
'id'=>1,
'name'=>'test'
],
[
'id'=>2,
'name'=>'test2'
]
];
and in my getContent(): return (Tools::getValue('options'));
But with that, nothing is displayed.
Also, if I do return sizeof(Tools::getValue('options)) It give me 1 no matter what I check with the checkbox
First you need to set the name of the field with []
$fields_form[0]['form']= [
'legend'=> [
'title'=> $this->l('Indexation')
] ,
'input'=>[
[
'type'=>'checkbox',
'label'=> $this->l('Base(s) à indexer'),
'name'=>'options[]',
'required'=>true,
'values'=>[
'query'=>$options,
'id'=>'id',
'name'=>'name'
]
]
],
'submit'=>[
'title' => $this->l('Save'),
'class' => 'btn btn-default pull-right'
]
];
Then, you options should have a value:
$options = [
[
'id'=>1,
'name'=>'test',
'val' => 1
],
[
'id'=>2,
'name'=>'test2',
'val' => 2
]
];
Then you can get the checked values with:
Tools::getValue('options')
Edit:
In 1.6 we have the admin tpl for the helper:
{foreach $input.values.query as $value}
{assign var=id_checkbox value=$input.name|cat:'_'|cat:$value[$input.values.id]}
<div class="checkbox{if isset($input.expand) && strtolower($input.expand.default) == 'show'} hidden{/if}">
{strip}
<label for="{$id_checkbox}">
<input type="checkbox" name="{$id_checkbox}" id="{$id_checkbox}" class="{if isset($input.class)}{$input.class}{/if}"{if isset($value.val)} value="{$value.val|escape:'html':'UTF-8'}"{/if}{if isset($fields_value[$id_checkbox]) && $fields_value[$id_checkbox]} checked="checked"{/if} />
{$value[$input.values.name]}
</label>
{/strip}
</div>
{/foreach}
So, to set the checkbox value to be returned we need to pass the val:
{if isset($value.val)} value="{$value.val|escape:'html':'UTF-8'}"{/if}
Also, to be checked or not when loading the page we pass the values to meet the criteria:
{if isset($fields_value[$id_checkbox]) && $fields_value[$id_checkbox]} checked="checked"{/if}
we are building our emails with mailJet which works fine so far. Our current problem is, that we would like to have a loop within a loop within a loop (so nested loops) the first loop works fine, and also the second inner loop is working fine, but the third one isn't working anymore:
{% for trip in var:trips %}
{{trip.id}}
{% for tripSegment in trip.tripSegments %}
{% for tripBreak in tripSegment.breaks %}
{{tripBreak.duration}}
{% endfor %}
{% endfor %}
Distance: {{trip.totalDistance}} km
{% endfor %}
This is in our template. If I remove the inner loop with the breaks, everything is fine.
If I enable the Template Error Reporting I get the following message:
X-MJ-ErrorMessage: "tripSegment.breaks" is not an array value
but if I try to print it with {{tripSegment.breaks}} I get the message:
X-MJ-ErrorMessage: Array values cannot be printed: tripSegment.breaks
and the field is definetly an array, like the trips or tripSegments, so basically it should work.
What am I doing wrong for nested loops in MailJet?
Edit:
My Object Structure, I send to Mailjet looks like this:
"Vars": {
"trips": [
"id": 1,
"tripSegments":[
{
"id":2,
"distance": 100,
"breaks":[
{
"duration":15
},{
"duration":20
}
]
},{
"id":3,
"distance": 200,
"breaks":[
{
"duration":10
},{
"duration":30
}
]
},{
"id":4,
"distance": 200,
"breaks":[
]
},
]
]
}
If you would like to loop on tripSegment.breaks, it should be represented as an array and not as an object.
I managed to create a sample which works for me with the same values you use.
'Vars' => [
"trips"=>[
'trips1' =>[
'id'=> 123,
'totalDistance'=> 10,
'tripSegments' => [
['breaks' =>[['duration'=>1],['duration'=> 2]]],
['breaks' =>[['duration'=>1],['duration'=> 2]]],
['breaks' =>[['duration'=>1],['duration'=> 2]]]
]
]
]
]
I find this has nothing to with the WYSIWYG Editor, remains a bug in Mailjet's templating language, and is easily reproducible. Use Postman or other API testing tool to send the following JSON to the send endpoint (make sure you add your API key credentials via Basic Auth):
POST https://api.mailjet.com/v3/send
{
"FromEmail": "me#example.com",
"FromName": "Me",
"Subject": "Test",
"MJ-TemplateLanguage": true,
"MJ-TemplateErrorReporting": "me#example.com",
"MJ-TemplateErrorDeliver": "deliver",
"Recipients": [
{ "Email": "me#example.com" }
],
"Html-part": "<ul>{% for project in var:commissions.projects %}<li>{{project.name}}</li>{% endfor %}</ul>",
"Vars": {
"commissions": { "total": "235,000", "projects": [] }
}
}
You'll get an error report via email back to you with the content No value for "commissions.projects" instead of it simply passing through the loop silently.
As we were in discussion with the MailJet Support [1], it turns out that there is a bug in the WYSIWYG editor of MailJet.
the multiple nested loops work, if you use them within a HTML Block and not directly in the WYSIWYG Editor.
They are working on that.
[1] https://app.mailjet.com/support/ticket/21e111b3be8630214cc082845f6cf976