2
\$\begingroup\$

I have a website where my users can create "table-like" layout dynamically. So imagine that my users can upload a text document, and then append columns to the document, and the output will then be parsed into a table-like layout.

For example:

My text document              Date: 20-06-2019                Created by: OJN

Content                       Nice Document                   Very!

Content                       Indeed!

                              Even more!

Now imagine that this is a word document for example, and my users have defined two columns on above (right between each block of text). This would output:

{
   "1":[
      {
         "row":"My text document"
      },
      {
         "row":"Content"
      },
      {
         "row":"Content"
      }
   ],
   "2":[
      {
         "row":"Date: 20-06-2019"
      },
      {
         "row":"Nice Document"
      },
      {
         "row": "Indeed!"
      },
      {
         "row": "Even more!"
      }
   ],
   "3":[
      {
         "row":"Created by: OJN"
      },
      {
         "row":"Very!"
      }
   ]
}

As you can see, this will ultimately generate three array objects with unequal elements.

0: Array(3)
1: Array(4)
2: Array(2)

Now when presenting this for the user in the frontend, I wish to show the parsed text content in a table, like: this

<table border="1">
  <tbody>
    <tr v-for="(row, index) in dataTable" :key="index">
      <td v-for="(cell, index) in row" :key="index">
        <span v-if="cell">{{cell["row"]}}</span>
      </td>
    </tr>
  </tbody>
</table>

However, as said, the arrays are of unequal length - so if I "just" transpose them, it will not generate the desired output. Instead, in above example, it will only create 3 rows in my table, because the first array contains 3 elements.

So in order to overcome this, I check the arrays to find the largest one:

// Transform object in array of its values
let data = Object.values(content);
let colsLength = data.map(r => r.length);
let maxNumCols = Math.max.apply(Math, colsLength);
let recordArraySchema = Array.apply(null, Array(maxNumCols)).map((r, i) => i);

this.dataTable = recordArraySchema.map((col, i) => data.map(row => row[i]));

This way, the array transposing will transpose all "rows", and fill the "empty"/non-existing will null, resulting in a full table.

I was wondering if this approach is acceptable in terms of performance and readability? I do have the option to change the JSON string format all together on the backend if that is more appropriate. However, I do have other backend functions that relies on the current schema, so those would have to be updated as well.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

The first thing I noticed is that the format of the data is a collection of columns with rows. The variable maxNumCols seems mis-leading. A more appropriate name for that variable would be maxNumRows because it describes how many rows are needed.

In terms of reducing the transformations needed, one option to consider is finding the maximum number of rows in a column and using that with v-for _with a Range to determine how many rows to add. Then looping over the content would eliminate the need to create recordArraySchema and dataTable - which would reduce the complexity from \$O(n^2)\$ to just \$O(n)\$.

<table border="1">
  <tbody>
    <tr v-for="r in maxNumRows">
      <td v-for="(column, index) in content" :key="index">
        <span v-if="column[r - 1]">{{column[r - 1].row}}</span>
      </td>
    </tr>
  </tbody>
</table>

Obviously this would require saving maxNumRows in the data collection.

Instead of using Function.apply() the spread syntax can be used to determine the maximum number of rows.

const rowLengths = data.map(r => r.length);
this.maxNumRows = Math.max(...rowLengths);

And instead of using let for variables that are only assigned once, it is wise to use const and then if it is determined that it needs to be re-assigned, use let. This helps avoid accidental re-assignment.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.