Iterate through objects in Blaze

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