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.
1 Answer 1
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.
Explore related questions
See similar questions with these tags.