Hi!
I have a data structure that is a nested object. It’s a map that has unique id keys.
Each key then contains another map which consists of another set of unique id keys (_id corresponding to Mongo documents). And each one of these keys contains a Mongo Document.
https://imgur.com/a/EjErAoF is an example of how the data structure looks like in Chrome dev tools.
I want to iterate over this in Blaze. My goal is to have a header that displays the top-level keys (1 & 2 in this example)…and then for each top level-key, have a table where each row is a nested document.
I understand that Blaze can only iterate through arrays and cursors and not JS Objects. And so I used _.pair to serialize my nested object into an array and here’s my result
https://imgur.com/a/bnwzYNk
I’m having trouble getting what I want with the 2D array though. Because now my top-level key (1 & 2 in this example) are at the first index of an array. I can’t figure out how to access this inside an each.
Here’s my full goal in a nutshell
I.e.
{{#each 2dArray}}
print {{this[0]}}
<table>
{{#each this[1]}}
<tr><td>{{nestedThis.data}}</td><tr>
{{/each}}
</table>
{{/each}}
Does anybody know how to do this or is there a better way to structure my data too? Thanks
I’ve done some interesting things with custom publications where I’ve pulled data out of Mongo then reformatted before publishing it in a simpler format to consume by my view layer in Blaze
I have also pulled back data as is from the database into the view layer then in a helper function transform it into something the template can easily consume via {{each}} etc.
Might either of those approaches work in your case?
You could create some helpers
Template.singularTemplate.helpers
outer2dDocuments: -> Object.values(2dArray)
innerDocuments: (nested) -> Object.values(nested)
plusOne: (i) -> i + 1
and use them like
{{#each outerDoc in outer2dDocuments}}
print {{plusOne @index}}
<table>
{{#each innerDoc in outerDoc}}
<tr><td>{{innerDoc.data}}</td><tr>
{{/each}}
</table>
{{/each}}
plusOne
ignores the the value from 2dArray
and instead uses @index
from the each
loop. If that doesn’t work you could take a look at Object.entries.
Helper functions are inneficient since they are called whenever the page re-renders.
Here’s my answer copied from your question on StackOverflow:
In your component, restructure the input into a new ReactiveVar
. Do this in an $autorun
, assuming the input is reactive:
Template.my_template.onCreated({
this.formattedData = new ReactiveVar([]);
this.$autorun(() => {
const input = this.input; // get this from a mongo query?...
this.formattedData = Object.keys(input).map(key => {
return {
key: key,
data: Object.values(input[key]).map(row => {
return Object.keys(row).map(k => {
return {key: k, data: row[k]};
});
})
}
})
})
})
Then you can display it using blaze:
{{#each formattedData}}
print {{key}}
<table>
{{#each data}}
<tr>
{{#each this}}
<td>{{key}}: {{data}}</td>
{{/each}}
</tr>
{{/each}}
</table>
{{/each}}
1 Like
Thanks so much! I’ve accepted your answer on SO