Mathias
Mathias10mo ago

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: 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
},
})
I would like to end up with data like this:
[
{ 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 */ ] },
]
So I can render it like this:
<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
erquhart
erquhart10mo ago
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:
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
},
})
Rendering would look like:
<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>
Mathias
MathiasOP10mo ago
@erquhart Thank you very much! It was much simpler than I thought. Good stuff!

Did you find this page helpful?