Find object by id in an array of JSON objects with Twig - json

I am being passed an array that unfortunately I cannot restructure:
"options": [
{"name":"namea","text":"valuea"},
{"name":"nameb","text":"valueb"},
{"name":"namec","text":"valuec"},
{"name":"named","text":"valued"}
]
I need to be able to find the object with the name equal to namea, nameb, namec, etc. and then produce the appropriate text. I tried the following and few other variations of that but could not get it to work:
{% for item in event.options %}
{% if item.name == "nameb" %}
{{ item.text|capitalize }}
{% endif %}
{% endfor %}
Thanks in advance for any help.
Edit: I basically only have access to a text box to input HTML and Twig. I do not have access to framework, custom extensions, etc.
Edit: JSON with exact formatting:
{
"source": "TEST.COM",
"trigger": "test",
"options": [{
"name":"test_date",
"value":"31-05-2017"
},
{
"name":"test_number",
"value":"9081003"
},
{
"name":"test_test",
"value":"9asd003"
},
{
"name":"Name",
"value":"Todd"
},
{
"name":"test_other",
"value":"kslkjsfd"
},
{
"name":"test_help",
"value":"908sdf3"
}]
}

This is going to be very hacky either way (I feel the javascript way as mentioned in the comments would still be less hacky).
However as its multilined we could write a basic parser like thing (this is by no means bullet proof and fails if the content has a ", or the structure changes or whatever changes to be honest):
{# create an array of lines #}
{% set event = event|split('\n') %}
{# an array to hold our options #}
{% set options = [] %}
{# using a bool to track if we are inside the options part #}
{% set inOptions = false %}
{# this holds the name of the last name value we have passed #}
{% set currentKey = '' %}
{# go through each line #}
{% for line in event %}
{# if we are inside the options tag do our magic #}
{% if inOptions %}
{# check if the line starts with "name": and track the current key name #}
{# or check if it starts with "value": and set the current key to that value #}
{% if line matches '/"name":/' %}
{# split line on the double quotes and get the last bit #}
{% set currentKey = line|split('"') %}
{# cant get the array index piped in one go idk.. #}
{% set currentKey = currentKey[3] %}
{% elseif line matches '/"value":/i' %}
{% set currentValue = line|split('"') %}
{% set options = options|merge({(currentKey): currentValue[3]}) %}
{% elseif line matches '/\s*}]/' %}
{% set inOptions = false %}
{% endif %}
{% endif %}
{# check for the options sectioning start #}
{% if line matches '/^"options/' %}
{% set inOptions = true %}
{% endif %}
{% endfor %}
{{ dump(options) }}
This returns in my dump (which you didn't have so you cant see):
array:6 [▼
"test_date" => "31-05-2017"
"test_number" => "9081003"
"test_test" => "9asd003"
"Name" => "Todd"
"test_other" => "kslkjsfd"
"test_help" => "908sdf3"
]
As an actual array where you can then call options.test_date but all this is super hacky, it still can be improved but twig is not made for it and the syntax gets so clunky its hard to maintain.

Related

loop through a for statement using an if/else with nunjucks and json

I'm trying to loop through a nested json file with nunjucks, give each object type a specific layout and sort all based on date.
So in my case I have two collections events and videos. Both collections will have an array of events and videos.
My file is named /content.json and structured as followed:
{
media: {
events: [
{
content_id: "1",
content_type: "event",
date: "01-11-2019",
etc: "etc"
},
{
content_id: "2",
content_type: "event",
date: "01-08-2019",
etc: "etc"
}
],
videos: [
{
content_id: "3",
content_type: "video",
date: "01-12-2019",
etc: "etc"
},
{
content_id: "4",
content_type: "video",
date: "01-09-2019",
etc: "etc"
}
]
}
}
I have tried to get the different object assigned with an if/else statement and then use a for loop to cycle through the array, but that has failed, see below:
{% for item in content.media %}
{% if item == events %}
{% for item in content.media.events %}
{% include "components/event.njk" %}
{% endfor %}
{% elif item == video %}
{% for item in content.media.videos %}
{% include "components/video.njk" %}
{% endfor %}
{% endif %}
{% endfor %}
I never got to try and sort all the content by date, but I have found:
{% for item in items|sort(attribute='date')%}
Can anyone guide me in right direction?
Thanks in advance.
AENM
This code outputs separated feeds by type of elements (event or video).
{% for event in content.media.events | sort(attribute = 'date') %}
{% include "components/event.njk" %}
{% endfor %}
{% for video in content.media.videos | sort(attribute = 'date') %}
{% include "components/video.njk" %}
{% endfor %}
If you need to output a mixed feed, you should join arrays to one and run trought it (fortunately each elements already have the type):
{% for item in [].concat(content.media.events, content.media.videos) | sort(attribute = 'date') %}
{% include "components/" + item.content_type + ".njk" %}
{% endfor %}
Aikon,
I got it now!!
It was another typo, you switched the media and events. (did you try to keep me sharp?! :-))
But that's why the concat didn't work!!
So this is the final working result:
{% for item in [].concat(media.content.events, media.content.videos) | sort(attribute = 'date') %}
{% include "components/" + item.type + ".njk" %}
{% endfor %}
Only the date is not in the correct order, but I think that has to with my grid setup.
Thanks for helping me out......

jekyll assign concat in a loop?

I would like to organize a page based on the number of pages that pass a filter.
I have tried to append truthy pages to a collection but it doesn't work.
{% assign homepage_posts = [] %}
{% for my_page in site.pages %}
{% if my_page.homepage %}
{% assign homepage_posts = homepage_posts | concat: [my_page] %}
{% endif %}
{% endfor %}
<h1>size{{homepage_posts.size}}</h1>
<h1>{{homepage_posts}}</h1>
This is not working. Does concat only work with strings?
Jekyll will use Liquid 4 soon. But, for now, no concat.
In your case you can :
Create an empty array (bracket notation doesn't work in liquid) : {% assign homepage_posts = "" | split:"/" %}
{{ homepage_posts | inspect }} --> output : []
And push elements in it :
{% for my_page in site.pages %}
{% if my_page.homepage %}
{% assign homepage_posts = homepage_posts | push: mypage %}
{% endif %}
{% endfor %}
{{ homepage_posts | inspect }}
concat filter only works with arrays and will be available in Jekyll when it upgrades to Liquid 4.*:
concat
Concatenates (combines) an array with another array. The resulting
array contains all the elements of the original arrays. concat will
not remove duplicate entries from the concatenated array unless you
also use the uniq filter.
To filter pages containing a specific attribute (in this case homepage: true) you can use a where filter.
Having a page with front matter:
---
homepage: true
---
Then you can have the pages with the homepage: true attribute like:
{% assign homepages = site.pages | where:"homepage","true" %}

Is there a way in Liquid to use the contains operator to check for an exact match?

When searching an array for a match in a Liquid template, how do you call contains exactly? For example, if tag of a page may contain separable or non-separable, how do you find the pages that contain only the separable and not the non-separable tag? In my experience, the {% if post.tags contains 'separable' %} statement considers both cases.
Refer to the documentation you can use this filter
{% assign tags = post.tags | where:"tag","separable" %}
Loop through the array and check the values with a match operator. If it matches change a variable from false to true:
{% assign found_seperable = false %}
{% for tag in post.tags %}
{% if tag == 'separable' %}
{% assign found_seperable = true %}
{% endif %}
{% endfor %}
Then check the variable:
{% if found_seperable %}
do what you want if true
{% else %}
do what you want if false
{% endif %}

How to get datas with multiple variables in path with jekyll and liquid

At its most basic level I need to append a partial object path onto an existing object path. In this particular instance I can't use plugins.
Say you have an object path:
{{ site.data.grants.2015.Return.ReturnHeader.ReturnTypeCd }}
Which, of course, can also be referenced as follows:
{% assign var = "ReturnTypeCd" %}
{{ site.data.grants.2015.Return.ReturnHeader[var] }}
How would I go about adding additional levels of nesting to the variable?
{% assign xTest = "Return.ReturnHeader.ReturnTypeCd" %}
{{ site.data.grants.2015[xTest] }}
//does not work
I've played around with both dot and bracket notations and using append as well as capture, but can't seem to find a solution that works.
This works :
Data file is _data/grants.yml
"2015":
Return:
ReturnHeader:
ReturnTypeCd: "Et hop !"
Getting deep target with a "dotted" string :
{% assign dataPath = site.data.grants.2015 %}
{% assign target = "Return.ReturnHeader.ReturnTypeCd" %}
{% comment %} ++++ Transform target string to an array {% endcomment %}
{% assign labels = target | split:"." %}
{% comment %} ++++
Looping in labels array and reassigning dataPath on each loop.
This goes deeper and deeper in the data tree
++++ {% endcomment %}
{% for label in labels %}
<h2>Label : {{ label }}</h2>
{% assign dataPath = dataPath[label] %}
<p>dataPath : {{ dataPath }}</p>
{% endfor %}

Pass variables from child template to parent in Jinja2

I want to have one parent template and many children templates with their own variables that they pass to the parent, like so:
parent.html:
{% block variables %}
{% endblock %}
{% if bool_var %}
{{ option_a }}
{% else %}
{{ option_b }}
{% endif %}
child.html:
{% extends "parent.html" %}
{% block variables %}
{% set bool_var = True %}
{% set option_a = 'Text specific to this child template' %}
{% set option_b = 'More text specific to this child template' %}
{% endblock %}
But the variables end up undefined in the parent.
Ah. Apparently they won't be defined when they are passed through blocks. The solution is to just remove the block tags and set it up like so:
parent.html:
{% if bool_var %}
{{ option_a }}
{% else %}
{{ option_b }}
{% endif %}
child.html:
{% extends "parent.html" %}
{% set bool_var = True %}
{% set option_a = 'Text specific to this child template' %}
{% set option_b = 'More text specific to this child template' %}
If Nathron's solution does not fix your problem, you can use a function in combination with a global python variable to pass a variable value.
Advantage: The variable's value will available in all templates. You can set the variable inside a block.
Disadvantage: More overhead.
This is what I did:
child.j2:
{{ set_my_var('new var value') }}
base.j2
{% set my_var = get_my_var() %}
python code
my_var = ''
def set_my_var(value):
global my_var
my_var = value
return '' # a function returning nothing will print a "none"
def get_my_var():
global my_var
return my_var
# make functions available inside jinja2
config = { 'set_my_var': set_my_var,
'get_my_var': get_my_var,
...
}
template = env.get_template('base.j2')
generated_code = template.render(config)
In some cases, you can avoid this 'parameter-passing' by creating another variant of the parent that adds/removes some block and extends it instead.
{% extends [condition]|yesno:'parent_1.html,parent_2.html' %}