Reduce in Paginate produces an array inside an array

Reduce(
  Lambda(
    ['accumulator', 'ref'],
    Append(
      Get(Var('ref')),
      Var('accumulator')
    )
  ),
  [],
  Paginate(Documents(Collection('notifications')), {
    size: 2
  })
)

Will result to:

{
  after: [Ref(Collection("notifications"), "296811454570430977")],
  data: [
    [
      {
        ref: Ref(Collection("notifications"), "295665276532294149"),
        ts: 1619949211300000,
        data: {
          createdAt: "2021-04-12T11:33:50.342615Z",
          updatedAt: "2021-05-02T09:53:31.133472Z"
        }
      },
      {
        ref: Ref(Collection("notifications"), "295666276580196871"),
        ts: 1619943100860000,
        data: {
          createdAt: "2021-04-12T11:49:44.073589Z",
          updatedAt: "2021-05-02T08:11:40.677455Z"
        }
      }
    ]
  ]
}

Notice how data is an array containing an array.

The expected output is:

{
  after: [Ref(Collection("notifications"), "296811454570430977")],
  data: [
      {
        ref: Ref(Collection("notifications"), "295665276532294149"),
        ts: 1619949211300000,
        data: {
          createdAt: "2021-04-12T11:33:50.342615Z",
          updatedAt: "2021-05-02T09:53:31.133472Z"
        }
      },
      {
        ref: Ref(Collection("notifications"), "295666276580196871"),
        ts: 1619943100860000,
        data: {
          createdAt: "2021-04-12T11:49:44.073589Z",
          updatedAt: "2021-05-02T08:11:40.677455Z"
        }
      }
    ]
}

The reason I’m using Reduce is that in my app I’m doing Map and then Filter, I can combine those two loops by doing reduce but the output is unexpected.

It looks like the accumulator gets “upgraded” to a Page, since that’s what Paginate returns. That’s why the after cursor is part of the result.

The simplest change to make would be to use Select:

Reduce(
  Lambda(
    ['accumulator', 'ref'],
    Append(
      Get(Var('ref')),
      Var('accumulator')
    )
  ),
  [],
  Select(
    ["data"],
    Paginate(Documents(Collection('notifications')), { size: 2})
  )
)

Doing that drops the after cursor, so if you need to iterate over multiple pages, your original query would be better.

Hmm, then I think Reduce does not work well with Paginate, I think an update to make it work well with Paginate will be great, and if using it with Paginate, I should not be required to provide an initialValue because the Reduce should default to using the data in Page as the initialValue.

+1 to having a separate Fold operation from Reduce. The current FQL Reduce does work more like a Fold should, requiring the initial value. It could be useful to have an operation start accumulating on the first element of the provided Page/Array.

Note that the “upgrade to Page” is very intentional. If your into functional programming, think of a Page as a Monad – The Page “knows” how to perform Map/Filter/Reduce on the data field while preserving the pagination cursors, which is really cool IMHO.

But yea… the result you show is a bit clunky, though it is appropriate the way that Reduce is defined. The most appropriate functional-programming thing to remove the extra array layer would probably be to wrap the result in a Flatten or FlatMap type call, but neither of those exist in FQL.

If you need to the pagination cursors, it looks like the best thing is to Select(['data', 0], ...) from the whole result where you need it. If you don’t need the cursors, then Ewan’s example is a good one.

1 Like

In my case, I really needed the cursor so I ended up doing Select(['data', 0], ...) :frowning: which made me sad.

Fold is a pretty clever name haha I like it, especially it operates on Page, that’s pretty cool, hoping for it to be implemented soon!

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.