#Issue with filtering out nulls from a promise.all mapping over some document ids

11 messages · Page 1 of 1 (latest)

barren lava
#
export const getById = zQuery({
  args: { id: zid('videoCollection') },
  handler: async (ctx, { id }) => {
    // Get the collection
    const collection = await ctx.db.get(id)
    if (!collection) return null

    // Get the videos from the collection videos
    const videos = await Promise.all(collection.videos.map((videoId) => ctx.db.get(videoId))).then(
      (results) => results.filter((r) => r !== null),
    ) // <- ISSUE: this currently returns null, even with the filter, because ctx.db.get() can return null

    // Enrich the videos
    const enrichedVideos = await Promise.all(videos.map(async (video) => enrichVideo(ctx, video)))

    // Return the collection including the videos
    return { title: collection.title, videos }
  },
})

Tried quite a few different methods of filtering out the nulls from this promise.all. Unsure if this approach is just generally not the way to go about it?

Am i on the wrong track here

#

Issue with filtering out nulls from a promise.all mapping over some document ids

barren lava
#

Hmm, this type guard seems to do the trick, still open for other potentially better solutions if anyone else has played around with this themselves:

 // Get the videos from the collection videos
  const videos = (
      await Promise.all(collection.videos.map((videoId) => ctx.db.get(videoId)))
    ).filter((video): video is NonNullable<Doc<'video'>> => video !== null)
urban knot
#

Just filter the whole array after mapping. You can also enrich in the initial mapping to pair things down:

export const getById = zQuery({
  args: { id: zid('videoCollection') },
  handler: async (ctx, { id }) => {
export const getById = zQuery({
  args: { id: zid('videoCollection') },
  handler: async (ctx, { id }) => {
    const collection = await ctx.db.get(id)
    if (!collection) return null

    const videos = (await Promise.all(
      collection.videos.map(async (videoId) => {
        const video = await ctx.db.get(videoId)
        if (video) {
          return enrichVideo(ctx, video)
        }
      }),
    )).filter(Boolean)

    return { title: collection.title, videos }
  },
})

  },
})
#

Wrapping the awaited Promise.all in parens allows us to filter directly on the resulting array. Boolean as a filter just drops everyting that isn't truthy.

barren lava
#

Actually i had tried the .filter(Boolean) at first, but you've just made me realise what was missing. The freaking async and await inside of the map

I had tried that before, storing it in a const and then checking if video, but my lack of making it async, still caused the null, so i moved onto trying a different solution 😆

It all makes sense now - thanks mate

barren lava
#

Ended up adding a type predicate, so only valid videos are getting returned:

function isValidVideo(video: any): video is VideoEnriched {
  return video !== undefined && video !== null
}
urban knot
#

Were you still getting nulls or was it an issue with the enrich function

barren lava
#

Getting undefined was the issue. From what i could tell, the map just returned undefined if video was false

#

And i wanted to make sure i got back neither undefined videos or null. But only valid videos

#

Even though the type guard works perfectly fine. Cant help but to think whether I’m just completely misunderstanding something here.