How to group tasks by their column?
I'm trying to group tasks by their respective column by doing a Map(), but I get the following error:
I would like to end up with data like this:
So I can render it like this:
Uncaught Error: Map[] is not a supported Convex type.
This is what I have tried:
export const getTasksGroupedByColumns = query({
args: { boardId: v.id("boards") },
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity()
if (!identity) {
throw new Error("Not authenticated")
}
const columns = await ctx.db
.query("columns")
.withIndex("by_board", (q) => q.eq("boardId", args.boardId))
.collect()
const columnIds = columns.map((column) => column._id)
const tasksByColumnId = await Promise.all(
columnIds.map(async (columnId) => {
const tasks = await ctx.db
.query("tasks")
.withIndex("by_column", (q) => q.eq("columnId", columnId))
.collect()
return { columnId, tasks }
})
)
const result = new Map()
columns.forEach((column) => {
const { tasks } = tasksByColumnId.find(
({ columnId }) => columnId === column._id
) || { tasks: [] }
result.set(column._id, {
...column,
tasks,
})
})
return result
},
})
export const getTasksGroupedByColumns = query({
args: { boardId: v.id("boards") },
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity()
if (!identity) {
throw new Error("Not authenticated")
}
const columns = await ctx.db
.query("columns")
.withIndex("by_board", (q) => q.eq("boardId", args.boardId))
.collect()
const columnIds = columns.map((column) => column._id)
const tasksByColumnId = await Promise.all(
columnIds.map(async (columnId) => {
const tasks = await ctx.db
.query("tasks")
.withIndex("by_column", (q) => q.eq("columnId", columnId))
.collect()
return { columnId, tasks }
})
)
const result = new Map()
columns.forEach((column) => {
const { tasks } = tasksByColumnId.find(
({ columnId }) => columnId === column._id
) || { tasks: [] }
result.set(column._id, {
...column,
tasks,
})
})
return result
},
})
[
{ column: { /* column data */ }, tasks: [ /* array of tasks */ ] },
column: { /* column data */ }, tasks: [ /* array of tasks */ ] },
]
[
{ column: { /* column data */ }, tasks: [ /* array of tasks */ ] },
column: { /* column data */ }, tasks: [ /* array of tasks */ ] },
]
<div>
{Array.from(columns).map(([columnId, { name, tasks }]) => (
<div key={columnId}>
<h3>{name}</h3>
{tasks.map((task) => (
<div key={task.id}>{task.title}</div>
))}
</div>
))}
</div>
<div>
{Array.from(columns).map(([columnId, { name, tasks }]) => (
<div key={columnId}>
<h3>{name}</h3>
{tasks.map((task) => (
<div key={task.id}>{task.title}</div>
))}
</div>
))}
</div>
2 Replies
You'll want to use an array of objects instead of a Map, since Map isn't a supported return value for Convex functions.
Something like:
Rendering would look like:
export const getTasksGroupedByColumns = query({
args: { boardId: v.id("boards") },
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity()
if (!identity) {
throw new Error("Not authenticated")
}
const columns = await ctx.db
.query("columns")
.withIndex("by_board", (q) => q.eq("boardId", args.boardId))
.collect()
const columnsWithTasks = await Promise.all(columns.map(async column => {
const tasks = await ctx.db
.query("tasks")
.withIndex("by_column", (q) => q.eq("columnId", columnId))
.collect()
return { ...column, tasks }
}))
return columnsWithTasks
},
})
export const getTasksGroupedByColumns = query({
args: { boardId: v.id("boards") },
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity()
if (!identity) {
throw new Error("Not authenticated")
}
const columns = await ctx.db
.query("columns")
.withIndex("by_board", (q) => q.eq("boardId", args.boardId))
.collect()
const columnsWithTasks = await Promise.all(columns.map(async column => {
const tasks = await ctx.db
.query("tasks")
.withIndex("by_column", (q) => q.eq("columnId", columnId))
.collect()
return { ...column, tasks }
}))
return columnsWithTasks
},
})
<div>
{columnsWithTasks.map(column => (
<div key={column._id}>
<h3>{column.name}</h3>
{column.tasks.map((task) => (
<div key={task._id}>{task.title}</div>
))}
</div>
))}
</div>
<div>
{columnsWithTasks.map(column => (
<div key={column._id}>
<h3>{column.name}</h3>
{column.tasks.map((task) => (
<div key={task._id}>{task.title}</div>
))}
</div>
))}
</div>
@erquhart Thank you very much! It was much simpler than I thought. Good stuff!