I'm a bit of a novice coder and I'm struggling to get some data to display in a table on a web page. This feels like it should be basic, but I just can't quite get the display right.
The data in the DB is in the following format, with each row containing data that will need to become a column heading and a row heading in the final rendered table.
Code,Ate,Round,Value,Active
ZOM3,Ears,16,18,True
ZOM4,Ears,16,22,True
ZOM4,Knees,16,24,True
ZOM4,Arms,16,90,True
ZOM4,Brains,16,940,True
ZOM4,Ears,17,29,True
ZOM4,Arms,17,114,True
ZOM4,Brains,17,746,True
ZOM4,Ears,18,23,True
ZOM4,Shins,18,1930,True
ZOM4,Arms,18,135,True
ZOM4,Brains,18,952,True
ZOM4,Ears,19,22,True
ZOM4,Shins,19,580,True
ZOM4,Knees,19,32,True
ZOM4,Arms,19,139,True
ZOM5,Shins,18,14,True
For character ZOM4, the way I need this to present on a web page is in a table like this:
16 | 17 | 18 | 19 | |
---|---|---|---|---|
Arms | 90 | 114 | 135 | 139 |
Brains | 940 | 746 | 952 | |
Ears | 22 | 29 | 23 | 22 |
Knees | 24 | 32 | ||
Shins | 1930 | 580 |
Sadly, my code does not. In the .vue file I have the following code, which ignores the null/missing Value values so that ZOM4 displays 1930 Shins in round 16.
const eats = computed(() => {
if (!props.data) return {};
const d = {} as { [key: string]: { rounds: { [key: string]: any } } };
for (let x of props.data.roundlyData) {
if (!d[x.ate]) {
d[x.ate] = { rounds: {} };
}
d[x.ate].rounds[x.round] = x.value;
}
return d;
});
const rounds = computed(() => {
const t = Object.keys(eats.value)[0];
if (!t) return [];
return Object.keys(eats.value[t].rounds);
});
</script>
<template>
<table > <!-- might need to sort the above data by ate and/or by round before presenting it. Test with Co. WPL -->
<colgroup>
<col style="width: 60px" />
</colgroup>
<thead>
<tr v-if="rounds">
<th></th>
<th v-for="round of rounds" :key="round">{{ round }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(t, ate) of eats" :key="ate">
<td>{{ ate }}</td>
<td v-for="r in t.rounds" :key="r">{{ r }}</td>
</tr>
</tbody>
</table>
</template>
This is driving me crazy. Can someone please help me get the table to display in a way that respects the nulls/missing data. Thanks
CodePudding user response:
Looks like your data is
arms.rounds=[16:90,17:114,18:135,19:139]
and
shins.rounds=[18:1930,19:580].
In your Loop
<td v-for="r in t.rounds" :key="r">{{ r }}</td> ....
you get just two entries for the shins, because you do not loop over [16,17,18,19] but the entries of rounds. So either ensure that the entries 16 and 17 are set to null in shins or loop over [16,17,18,19] and display the data accordingly if empty.
CodePudding user response:
Crunching data into tables is almost never basic. Here's how I would do it:
const { createApp } = Vue;
createApp({
setup() {
const initialData = `Code,Ate,Round,Value,Active
ZOM3,Ears,16,18,True
ZOM4,Ears,16,22,True
ZOM4,Knees,16,24,True
ZOM4,Arms,16,90,True
ZOM4,Brains,16,940,True
ZOM4,Ears,17,29,True
ZOM4,Arms,17,114,True
ZOM4,Brains,17,746,True
ZOM4,Ears,18,23,True
ZOM4,Shins,18,1930,True
ZOM4,Arms,18,135,True
ZOM4,Brains,18,952,True
ZOM4,Ears,19,22,True
ZOM4,Shins,19,580,True
ZOM4,Knees,19,32,True
ZOM4,Arms,19,139,True
ZOM5,Shins,18,14,True`;
const isNumeric = (num) => !isNaN(num);
const rawData = initialData
.split('\n')
.map(
entry => entry
.split(',')
.map(s => s.trim())
.map(n => isNumeric(n) ?
Number(n) :
['True', 'False'].includes(n) ?
n === 'True' :
n
)
);
const names = rawData.splice(0, 1)[0];
const rounds = [...new Set(rawData.map(a => a[2]))];
const entries = rawData.reduce((a, b) => [...a, Object.assign({},
...names.map(
(h, i) => ({
[h]: b[i]
})
)
)], []).reduce((a, { Code, ...rest }) => {
a[Code] = [...(a[Code] || []), rest];
return a
}, {});
return {
entries,
rounds,
getEntryRows: entry => [...new Set(entry.map(o => o.Ate))].sort(),
getEntryCell: ({
food,
name,
round
}) => entries[name]
.find(o => o.Ate === food && o.Round === round)
?.Value || '---'
}
}
}).mount('#app')
body {
font-family: sans-serif;
}
table {
width: 100%;
max-width: 800px;
border-collapse: collapse;
}
th[colspan] {
border-color: #ddd #fff;
font-size: 1.5rem;
padding: 2rem 0 1rem;
font-weight: normal;
}
td,
th {
padding: .5rem .75rem .25rem;
text-align: left;
border: 1px solid #ddd;
}
td:first-child {
padding-left: 2rem;
}
.rounds th {
width: 20%;
background-color: #f8f9f9;
}
.row-name {
display: flex;
align-items: center;
}
.row-name:before,
.row-name:after {
content: '';
flex-grow: 1;
margin: 0 1rem;
height: 0;
border-bottom: 1px solid #ddd;
}
.row-name:before {
margin-left: 0;
}
.row-name:after {
margin-right: 0;
flex-grow: 2.4;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.2.37/vue.global.prod.min.js"></script>
<div id="app">
<table>
<tbody>
<template v-for="(entry, name) in entries">
<tr :key="`h-${name}`">
<th :colspan="rounds.length 1">
<div v-text="name"></div>
</th>
</tr>
<tr :key="`r-${name}`" >
<th v-for="round in ['', ...rounds]" v-text="round"></th>
</tr>
<tr :key="`${row}-${name}`"
v-for="food in getEntryRows(entry)">
<td v-text="food"></td>
<td v-for="round in rounds"
:key="round"
v-text="getEntryCell({food, name, round})"></td>
</template>
</table>
</div>